@@ -1178,7 +1178,7 @@ MetadataNode::TypeMetadata* MetadataNode::TryGetTypeMetadata(Isolate* isolate, c
11781178 return nullptr ;
11791179 }
11801180
1181- return reinterpret_cast <TypeMetadata*>(hiddenVal.As <External>()->Value ());
1181+ return reinterpret_cast <TypeMetadata*>(hiddenVal.As <External>()->Value (v8:: kExternalPointerTypeTagDefault ));
11821182}
11831183
11841184std::string MetadataNode::TryResolveClassCtorTypeName (Isolate* isolate, const Local<Function>& func) {
@@ -1221,20 +1221,51 @@ std::string SanitizeESClassNamePart(const std::string& name) {
12211221 std::string result;
12221222 result.reserve (name.size ());
12231223 for (char c : name) {
1224- bool isValid = isalpha (c) || isdigit (c) || c == ' _' ;
1224+ auto uc = static_cast <unsigned char >(c);
1225+ bool isValid = isalnum (uc) || c == ' _' ;
12251226 result += isValid ? c : ' _' ;
12261227 }
12271228 return result;
12281229}
12291230
1231+ std::string BuildESClassProxyIdentity (const std::string& scriptName, const std::string& baseClassName, const std::string& className,
1232+ const std::vector<std::string>& methodOverrides, const std::vector<std::string>& implementedInterfaces) {
1233+ auto sortedMethods = methodOverrides;
1234+ auto sortedInterfaces = implementedInterfaces;
1235+ std::sort (sortedMethods.begin (), sortedMethods.end ());
1236+ std::sort (sortedInterfaces.begin (), sortedInterfaces.end ());
1237+
1238+ std::string identity = scriptName + " |" + baseClassName + " |" + className;
1239+ for (const auto & method : sortedMethods) {
1240+ identity += " |m:" + method;
1241+ }
1242+ for (const auto & iface : sortedInterfaces) {
1243+ identity += " |i:" + iface;
1244+ }
1245+ return identity;
1246+ }
1247+
1248+ bool TryGetConstructorPrototype (Isolate* isolate, const Local<Function>& ctorFunc, Local<Object>& out) {
1249+ auto context = isolate->GetCurrentContext ();
1250+ Local<Value> protoValue;
1251+ if (!ctorFunc->Get (context, V8StringConstants::GetPrototype (isolate)).ToLocal (&protoValue)
1252+ || protoValue.IsEmpty () || !protoValue->IsObject ()) {
1253+ return false ;
1254+ }
1255+ out = protoValue.As <Object>();
1256+ return true ;
1257+ }
1258+
12301259// True only for genuine `class` syntax constructors. Function source text is the reliable
12311260// discriminator: per spec, Function.prototype.toString for a class constructor reproduces the
12321261// `class` declaration/expression source (possibly behind leading comments/whitespace, which V8
12331262// does not emit for the class case - the text starts with "class").
12341263bool IsESClassConstructor (v8::Isolate* isolate, const v8::Local<v8::Function>& func) {
1264+ v8::TryCatch tc (isolate);
12351265 auto context = isolate->GetCurrentContext ();
12361266 v8::Local<v8::String> sourceText;
12371267 if (!func->FunctionProtoToString (context).ToLocal (&sourceText)) {
1268+ tc.Reset ();
12381269 return false ;
12391270 }
12401271
@@ -1431,7 +1462,7 @@ MetadataNode::TypeMetadata* MetadataNode::EnsureExtendedESClass(Isolate* isolate
14311462 scriptName = ArgConverter::ConvertToString (resourceName.As <String>());
14321463 }
14331464
1434- string extendNameAndLocation = " es" + HashESClassId (scriptName + " | " + baseClassName + " | " + className ) + " _" + className;
1465+ string extendNameAndLocation = " es" + HashESClassId (BuildESClassProxyIdentity ( scriptName, baseClassName, className, methodOverrides, implementedInterfaces) ) + " _" + className;
14351466 string candidate = TNS_PREFIX + CreateFullClassName (baseClassName, extendNameAndLocation);
14361467
14371468 // collision handling for distinct classes that produce the same deterministic name
@@ -1492,17 +1523,26 @@ bool MetadataNode::TryConstructESDerivedInstance(Isolate* isolate, const string&
14921523 return false ;
14931524 }
14941525
1526+ // DexFactory collapses every interface extension onto one shared
1527+ // com.tns.gen.<interface> proxy. That name does not identify a single ES
1528+ // class, so Java-born instances stay on the legacy wrapper path.
1529+ if (cacheData.node != nullptr && cacheData.node ->IsNodeTypeInterface ()) {
1530+ return false ;
1531+ }
1532+
14951533 Local<Function> ctor = Local<Function>::New (isolate, *cacheData.extendedCtorFunction );
14961534 auto typeMetadata = TryGetTypeMetadata (isolate, ctor);
14971535 if (typeMetadata == nullptr || !typeMetadata->isESDerived ) {
14981536 return false ;
14991537 }
15001538
15011539 cache->PendingESAdoptObjectId = javaObjectID;
1540+ cache->PendingESAdoptClassName = proxyClassName;
15021541 TryCatch tc (isolate);
15031542 auto context = isolate->GetCurrentContext ();
15041543 bool ok = !ctor->CallAsConstructor (context, 0 , nullptr ).IsEmpty ();
15051544 cache->PendingESAdoptObjectId = -1 ;
1545+ cache->PendingESAdoptClassName .clear ();
15061546 if (!ok) {
15071547 throw NativeScriptException (tc, " Failed to construct ES class for native instance" );
15081548 }
@@ -1517,14 +1557,18 @@ bool MetadataNode::TryConstructESDerivedInstance(Isolate* isolate, const string&
15171557 return true ;
15181558}
15191559
1520- bool MetadataNode::TryConsumePendingESAdopt (Isolate* isolate, int & javaObjectID) {
1560+ bool MetadataNode::TryConsumePendingESAdopt (Isolate* isolate, const string& fullClassName, int & javaObjectID) {
15211561 auto cache = GetMetadataNodeCache (isolate);
15221562 if (cache->PendingESAdoptObjectId == -1 ) {
15231563 return false ;
15241564 }
1565+ if (cache->PendingESAdoptClassName != fullClassName) {
1566+ return false ;
1567+ }
15251568
15261569 javaObjectID = cache->PendingESAdoptObjectId ;
15271570 cache->PendingESAdoptObjectId = -1 ;
1571+ cache->PendingESAdoptClassName .clear ();
15281572 return true ;
15291573}
15301574
@@ -1618,7 +1662,10 @@ void MetadataNode::InterfaceConstructorCallback(const v8::FunctionCallbackInfo<v
16181662 }
16191663
16201664 if (typeMetadata != nullptr && typeMetadata->isESDerived ) {
1621- auto esImplementationObject = newTargetFunc->Get (context, V8StringConstants::GetPrototype (isolate)).ToLocalChecked ().As <Object>();
1665+ Local<Object> esImplementationObject;
1666+ if (!TryGetConstructorPrototype (isolate, newTargetFunc, esImplementationObject)) {
1667+ throw NativeScriptException (string (" Cannot resolve the prototype of the ES class constructor." ));
1668+ }
16221669
16231670 SetInstanceMetadata (isolate, thiz, node);
16241671 thiz->SetInternalField (static_cast <int >(ObjectManager::MetadataNodeKeys::CallSuper), True (isolate));
@@ -1705,8 +1752,10 @@ void MetadataNode::ClassConstructorCallback(const v8::FunctionCallbackInfo<v8::V
17051752 }
17061753
17071754 if (typeMetadata != nullptr && typeMetadata->isESDerived ) {
1708- auto context = isolate->GetCurrentContext ();
1709- auto implementationObject = newTargetFunc->Get (context, V8StringConstants::GetPrototype (isolate)).ToLocalChecked ().As <Object>();
1755+ Local<Object> implementationObject;
1756+ if (!TryGetConstructorPrototype (isolate, newTargetFunc, implementationObject)) {
1757+ throw NativeScriptException (string (" Cannot resolve the prototype of the ES class constructor." ));
1758+ }
17101759
17111760 SetInstanceMetadata (isolate, thiz, node);
17121761 thiz->SetInternalField (static_cast <int >(ObjectManager::MetadataNodeKeys::CallSuper), True (isolate));
0 commit comments