Skip to content

Commit 4c131f4

Browse files
committed
fix(ffi): re-adopt init result on host object for JS override return-this
When a JS override calls super.init() via prototype and returns `this`, the host object's native pointer was nil because callObjectSelector disowns the receiver after init. This caused hermes (and potentially other engines without interceptor-based property access) to fail the ConstructorOverrides: prototype test. Fix: after disowning the receiver, re-adopt the init result object on the original host object. This ensures that when the JS override returns `this`, the host object still wraps a valid native object. Tests: all engines 713/0 (hermes was 712/1, now fixed)
1 parent b526139 commit 4c131f4

1 file changed

Lines changed: 34 additions & 20 deletions

File tree

NativeScript/ffi/shared/bridge/HostObjects.mm

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -681,16 +681,23 @@ throw JSError(runtime,
681681
if (initializer) {
682682
id resultObject = nativeObjectFromValue(runtime, result);
683683
disownObject(receiver);
684-
if (resultObject != nil && classWrapper) {
685-
bridge_->setObjectExpando(runtime, resultObject,
686-
"__nativeApiClassWrapper",
687-
Value(runtime, *classWrapper));
688-
if (result.isObject()) {
689-
Value prototypeValue = classWrapper->getProperty(runtime, "prototype");
690-
if (prototypeValue.isObject()) {
691-
Object resultValue = result.asObject(runtime);
692-
Object prototype = prototypeValue.asObject(runtime);
693-
SetNativeApiObjectPrototype(runtime, resultValue, prototype);
684+
if (resultObject != nil) {
685+
// Re-adopt the init result on this host object so that JS overrides
686+
// returning `this` still have a valid native object.
687+
object_ = resultObject;
688+
ownsObject_ = true;
689+
[object_ retain];
690+
if (classWrapper) {
691+
bridge_->setObjectExpando(runtime, resultObject,
692+
"__nativeApiClassWrapper",
693+
Value(runtime, *classWrapper));
694+
if (result.isObject()) {
695+
Value prototypeValue = classWrapper->getProperty(runtime, "prototype");
696+
if (prototypeValue.isObject()) {
697+
Object resultValue = result.asObject(runtime);
698+
Object prototype = prototypeValue.asObject(runtime);
699+
SetNativeApiObjectPrototype(runtime, resultValue, prototype);
700+
}
694701
}
695702
}
696703
}
@@ -726,16 +733,23 @@ throw JSError(runtime,
726733
if (initializer) {
727734
id resultObject = nativeObjectFromValue(runtime, result);
728735
disownObject(receiver);
729-
if (resultObject != nil && classWrapper) {
730-
bridge_->setObjectExpando(runtime, resultObject,
731-
"__nativeApiClassWrapper",
732-
Value(runtime, *classWrapper));
733-
if (result.isObject()) {
734-
Value prototypeValue = classWrapper->getProperty(runtime, "prototype");
735-
if (prototypeValue.isObject()) {
736-
Object resultValue = result.asObject(runtime);
737-
Object prototype = prototypeValue.asObject(runtime);
738-
SetNativeApiObjectPrototype(runtime, resultValue, prototype);
736+
if (resultObject != nil) {
737+
// Re-adopt the init result on this host object so that JS overrides
738+
// returning `this` still have a valid native object.
739+
object_ = resultObject;
740+
ownsObject_ = true;
741+
[object_ retain];
742+
if (classWrapper) {
743+
bridge_->setObjectExpando(runtime, resultObject,
744+
"__nativeApiClassWrapper",
745+
Value(runtime, *classWrapper));
746+
if (result.isObject()) {
747+
Value prototypeValue = classWrapper->getProperty(runtime, "prototype");
748+
if (prototypeValue.isObject()) {
749+
Object resultValue = result.asObject(runtime);
750+
Object prototype = prototypeValue.asObject(runtime);
751+
SetNativeApiObjectPrototype(runtime, resultValue, prototype);
752+
}
739753
}
740754
}
741755
}

0 commit comments

Comments
 (0)