@@ -231,6 +231,9 @@ Value get(Runtime& runtime, const PropNameID& name) override {
231231 backingValue_(std::move(backingValue)) {}
232232
233233 ~NativeApiReferenceHostObject () override {
234+ for (id object : retainedObjects_) {
235+ [object release ];
236+ }
234237 if (ownsData_ && data_ != nullptr ) {
235238 free (data_);
236239 data_ = nullptr ;
@@ -242,6 +245,7 @@ Value get(Runtime& runtime, const PropNameID& name) override {
242245 std::shared_ptr<Value> backingValue () const { return backingValue_; }
243246 void ensureStorage (Runtime& runtime, NativeApiType type,
244247 NativeApiArgumentFrame& frame, size_t elements = 1 );
248+ void retainObjectSlot (size_t index, id object);
245249
246250 Value get (Runtime& runtime, const PropNameID& name) override ;
247251 NativeApiHostSetResult set (Runtime& runtime, const PropNameID& name, const Value& value) override ;
@@ -262,6 +266,7 @@ void ensureStorage(Runtime& runtime, NativeApiType type,
262266 size_t byteLength_ = 0 ;
263267 std::shared_ptr<Value> pendingValue_;
264268 std::shared_ptr<Value> backingValue_;
269+ std::vector<id > retainedObjects_;
265270};
266271
267272class NativeApiStructObjectHostObject final : public HostObject {
@@ -643,6 +648,9 @@ Array runtimeMembersArray(Runtime& runtime, Class cls, bool staticMembers) {
643648 object_ (object),
644649 ownsObject_(ownsObject),
645650 lifetimeState_(std::make_shared<NativeApiObjectLifetimeState>(object)) {
651+ if (bridge_ != nullptr && object_ != nil ) {
652+ bridge_->retainObjectExpandoOwner (object_);
653+ }
646654 if (object_ != nil && !ownsObject_) {
647655 [object_ retain ];
648656 ownsObject_ = true ;
@@ -653,7 +661,9 @@ Array runtimeMembersArray(Runtime& runtime, Class cls, bool staticMembers) {
653661 ~NativeApiObjectHostObject () override {
654662 if (bridge_ != nullptr && object_ != nil ) {
655663 bridge_->forgetRoundTripValue (object_);
656- bridge_->forgetObjectExpandos (object_);
664+ bridge_->releaseObjectExpandoOwner (
665+ object_, class_conformsToProtocol (object_getClass (object_),
666+ @protocol (NativeApiClassBuilderProtocol)));
657667 }
658668 if (lifetimeState_ != nullptr ) {
659669 lifetimeState_->clear ();
@@ -679,11 +689,11 @@ void storeOwnExpando(Runtime& runtime, const std::string& property,
679689 }
680690 }
681691
682- void disownObject (id expected) {
692+ void disownObject (id expected, bool preserveExpandos = false ) {
683693 if (object_ == expected) {
684694 if (bridge_ != nullptr && expected != nil ) {
685695 bridge_->forgetRoundTripValue (expected);
686- bridge_->forgetObjectExpandos (expected);
696+ bridge_->releaseObjectExpandoOwner (expected, preserveExpandos );
687697 }
688698 ownsObject_ = false ;
689699 wrapperRetainedObject_ = false ;
@@ -737,21 +747,23 @@ throw JSError(runtime,
737747 classWrapper.emplace (classWrapperValue.asObject (runtime));
738748 }
739749 bridge_->forgetRoundTripValue (runtime, receiver);
740- bridge_->forgetObjectExpandos (receiver);
741750 }
742751
743752 Value result =
744753 callObjCSelector (runtime, bridge_, receiver, false , selectorName, member,
745754 args, count, dispatchSuperClass);
746755 if (initializer) {
747756 id resultObject = nativeObjectFromValue (runtime, result);
748- disownObject (receiver);
757+ disownObject (receiver, resultObject == receiver );
749758 if (resultObject != nil ) {
750759 // Re-adopt the init result on this host object so that JS overrides
751760 // returning `this` still have a valid native object.
752761 object_ = resultObject;
753762 ownsObject_ = true ;
754763 wrapperRetainedObject_ = true ;
764+ if (bridge_ != nullptr ) {
765+ bridge_->retainObjectExpandoOwner (object_);
766+ }
755767 if (lifetimeState_ != nullptr ) {
756768 lifetimeState_->setObject (object_);
757769 }
@@ -792,21 +804,23 @@ throw JSError(runtime,
792804 classWrapper.emplace (classWrapperValue.asObject (runtime));
793805 }
794806 bridge_->forgetRoundTripValue (runtime, receiver);
795- bridge_->forgetObjectExpandos (receiver);
796807 }
797808
798809 Value result = callPreparedObjCSelector (
799810 runtime, bridge_, receiver, false , prepared, args, count,
800811 dispatchSuperClass);
801812 if (initializer) {
802813 id resultObject = nativeObjectFromValue (runtime, result);
803- disownObject (receiver);
814+ disownObject (receiver, resultObject == receiver );
804815 if (resultObject != nil ) {
805816 // Re-adopt the init result on this host object so that JS overrides
806817 // returning `this` still have a valid native object.
807818 object_ = resultObject;
808819 ownsObject_ = true ;
809820 wrapperRetainedObject_ = true ;
821+ if (bridge_ != nullptr ) {
822+ bridge_->retainObjectExpandoOwner (object_);
823+ }
810824 if (lifetimeState_ != nullptr ) {
811825 lifetimeState_->setObject (object_);
812826 }
@@ -829,9 +843,8 @@ throw JSError(runtime,
829843 return result;
830844 }
831845
832- Value prototypeFunctionForProperty (Runtime& runtime,
833- const std::string& property) {
834- if (object_ == nil || property.empty ()) {
846+ Value classPrototypeForObject (Runtime& runtime) {
847+ if (object_ == nil ) {
835848 return Value::undefined ();
836849 }
837850
@@ -847,12 +860,32 @@ Value prototypeFunctionForProperty(Runtime& runtime,
847860 runtime, objc_lookUpClass (symbol->runtimeName .c_str ()));
848861 }
849862 }
850- if (!classWrapperValue.isObject ()) {
863+ if (classWrapperValue.isObject ()) {
864+ Object classWrapper = classWrapperValue.asObject (runtime);
865+ Value prototypeValue = classWrapper.getProperty (runtime, " prototype" );
866+ if (prototypeValue.isObject ()) {
867+ return prototypeValue;
868+ }
869+ }
870+ return bridge_->findClassPrototype (runtime, object_getClass (object_));
871+ }
872+
873+ Value engineThisValueForObject (Runtime& runtime) {
874+ Value thisValue = bridge_->findRoundTripValue (runtime, object_,
875+ nullptr , true );
876+ if (thisValue.isObject ()) {
877+ return thisValue;
878+ }
879+ return makeNativeObjectValue (runtime, bridge_, object_, false );
880+ }
881+
882+ Value prototypeFunctionForProperty (Runtime& runtime,
883+ const std::string& property) {
884+ if (property.empty ()) {
851885 return Value::undefined ();
852886 }
853887
854- Object classWrapper = classWrapperValue.asObject (runtime);
855- Value prototypeValue = classWrapper.getProperty (runtime, " prototype" );
888+ Value prototypeValue = classPrototypeForObject (runtime);
856889 if (!prototypeValue.isObject ()) {
857890 return Value::undefined ();
858891 }
@@ -897,17 +930,7 @@ Value resolveEnginePrototypeGetter(Runtime& runtime,
897930 if (object_ == nil || property.empty ()) {
898931 return Value::undefined ();
899932 }
900- Value classWrapperValue =
901- bridge_->findObjectExpando (runtime, object_, " __nativeApiClassWrapper" );
902- if (!classWrapperValue.isObject ()) {
903- classWrapperValue =
904- bridge_->findClassValue (runtime, object_getClass (object_));
905- }
906- if (!classWrapperValue.isObject ()) {
907- return Value::undefined ();
908- }
909- Value prototypeValue =
910- classWrapperValue.asObject (runtime).getProperty (runtime, " prototype" );
933+ Value prototypeValue = classPrototypeForObject (runtime);
911934 if (!prototypeValue.isObject ()) {
912935 return Value::undefined ();
913936 }
@@ -928,8 +951,7 @@ Value resolveEnginePrototypeGetter(Runtime& runtime,
928951 Value getterValue = descriptor.getProperty (runtime, " get" );
929952 if (getterValue.isObject () &&
930953 getterValue.asObject (runtime).isFunction (runtime)) {
931- Value thisValue = bridge_->findRoundTripValue (runtime, object_,
932- nullptr , true );
954+ Value thisValue = engineThisValueForObject (runtime);
933955 if (thisValue.isObject ()) {
934956 *found = true ;
935957 return getterValue.asObject (runtime).asFunction (runtime).callWithThis (
@@ -956,17 +978,7 @@ bool invokeEnginePrototypeSetter(Runtime& runtime, const std::string& property,
956978 if (object_ == nil || property.empty ()) {
957979 return false ;
958980 }
959- Value classWrapperValue =
960- bridge_->findObjectExpando (runtime, object_, " __nativeApiClassWrapper" );
961- if (!classWrapperValue.isObject ()) {
962- classWrapperValue =
963- bridge_->findClassValue (runtime, object_getClass (object_));
964- }
965- if (!classWrapperValue.isObject ()) {
966- return false ;
967- }
968- Value prototypeValue =
969- classWrapperValue.asObject (runtime).getProperty (runtime, " prototype" );
981+ Value prototypeValue = classPrototypeForObject (runtime);
970982 if (!prototypeValue.isObject ()) {
971983 return false ;
972984 }
@@ -987,8 +999,7 @@ bool invokeEnginePrototypeSetter(Runtime& runtime, const std::string& property,
987999 descriptorValue.asObject (runtime).getProperty (runtime, " set" );
9881000 if (setterValue.isObject () &&
9891001 setterValue.asObject (runtime).isFunction (runtime)) {
990- Value thisValue = bridge_->findRoundTripValue (runtime, object_,
991- nullptr , true );
1002+ Value thisValue = engineThisValueForObject (runtime);
9921003 if (thisValue.isObject ()) {
9931004 Value args[] = {Value (runtime, value)};
9941005 setterValue.asObject (runtime).asFunction (runtime).callWithThis (
@@ -1190,7 +1201,7 @@ Value get(Runtime& runtime, const PropNameID& name) override {
11901201 bool wrapperRetainedObject = self->wrapperRetainedObject_ ;
11911202 if (self->bridge_ != nullptr ) {
11921203 self->bridge_ ->forgetRoundTripValue (runtime, object);
1193- self->bridge_ ->forgetObjectExpandos (object);
1204+ self->bridge_ ->releaseObjectExpandoOwner (object);
11941205 }
11951206 self->object_ = nil ;
11961207 self->ownsObject_ = false ;
@@ -1491,7 +1502,9 @@ throw JSError(
14911502 // Engines whose exotic property storage doesn't fall back to own
14921503 // properties need the JS-owned set resolved here: invoke a JS-prototype
14931504 // setter if present, otherwise store the value as a bridge expando.
1494- if (!invokeEnginePrototypeSetter (runtime, property, value)) {
1505+ bool invokedPrototypeSetter =
1506+ invokeEnginePrototypeSetter (runtime, property, value);
1507+ if (!invokedPrototypeSetter) {
14951508 storeOwnExpando (runtime, property, value);
14961509 }
14971510 NATIVE_API_SET_RETURN (true );
0 commit comments