@@ -49,11 +49,13 @@ void setObject(id object) {
4949 public:
5050 NativeApiPointerHostObject (std::shared_ptr<NativeApiBridge> bridge,
5151 void * pointer, std::string kind = " pointer" ,
52- bool adopted = false )
52+ bool adopted = false ,
53+ std::shared_ptr<Value> backingValue = nullptr )
5354 : bridge_(std::move(bridge)),
5455 pointer_ (pointer),
5556 kind_(std::move(kind)),
56- adopted_(adopted) {}
57+ adopted_(adopted),
58+ backingValue_(std::move(backingValue)) {}
5759
5860 ~NativeApiPointerHostObject () override {
5961 if (adopted_ && pointer_ != nullptr ) {
@@ -66,6 +68,10 @@ void setObject(id object) {
6668 }
6769
6870 void * pointer () const { return pointer_; }
71+ std::shared_ptr<Value> backingValue () const { return backingValue_; }
72+ void setBackingValue (Runtime& runtime, const Value& value) {
73+ backingValue_ = std::make_shared<Value>(runtime, value);
74+ }
6975 bool adopted () const { return adopted_; }
7076 void adopt () { adopted_ = true ; }
7177 void clearWithoutFree () {
@@ -74,6 +80,7 @@ void clearWithoutFree() {
7480 }
7581 pointer_ = nullptr ;
7682 adopted_ = false ;
83+ backingValue_.reset ();
7784 }
7885
7986 Value get (Runtime& runtime, const PropNameID& name) override {
@@ -102,6 +109,7 @@ Value get(Runtime& runtime, const PropNameID& name) override {
102109 self->consumed_ = true ;
103110 self->pointer_ = nullptr ;
104111 self->adopted_ = false ;
112+ self->backingValue_ .reset ();
105113 return makeNativeObjectValue (runtime, self->bridge_ , object, retained);
106114 });
107115 }
@@ -204,6 +212,7 @@ Value get(Runtime& runtime, const PropNameID& name) override {
204212 std::string kind_;
205213 bool adopted_ = false ;
206214 bool consumed_ = false ;
215+ std::shared_ptr<Value> backingValue_;
207216};
208217
209218class NativeApiReferenceHostObject final : public HostObject {
@@ -230,6 +239,7 @@ Value get(Runtime& runtime, const PropNameID& name) override {
230239
231240 void * data () const { return data_; }
232241 const NativeApiType& type () const { return type_; }
242+ std::shared_ptr<Value> backingValue () const { return backingValue_; }
233243 void ensureStorage (Runtime& runtime, NativeApiType type,
234244 NativeApiArgumentFrame& frame, size_t elements = 1 );
235245
@@ -640,6 +650,10 @@ Array runtimeMembersArray(Runtime& runtime, Class cls, bool staticMembers) {
640650 }
641651
642652 ~NativeApiObjectHostObject () override {
653+ if (bridge_ != nullptr && object_ != nil ) {
654+ bridge_->forgetRoundTripValue (object_);
655+ bridge_->forgetObjectExpandos (object_);
656+ }
643657 if (lifetimeState_ != nullptr ) {
644658 lifetimeState_->clear ();
645659 }
@@ -666,6 +680,10 @@ void storeOwnExpando(Runtime& runtime, const std::string& property,
666680
667681 void disownObject (id expected) {
668682 if (object_ == expected) {
683+ if (bridge_ != nullptr && expected != nil ) {
684+ bridge_->forgetRoundTripValue (expected);
685+ bridge_->forgetObjectExpandos (expected);
686+ }
669687 ownsObject_ = false ;
670688 object_ = nil ;
671689 if (lifetimeState_ != nullptr ) {
@@ -906,7 +924,8 @@ Value resolveEnginePrototypeGetter(Runtime& runtime,
906924 Value getterValue = descriptor.getProperty (runtime, " get" );
907925 if (getterValue.isObject () &&
908926 getterValue.asObject (runtime).isFunction (runtime)) {
909- Value thisValue = bridge_->findRoundTripValue (runtime, object_);
927+ Value thisValue = bridge_->findRoundTripValue (runtime, object_,
928+ nullptr , true );
910929 if (thisValue.isObject ()) {
911930 *found = true ;
912931 return getterValue.asObject (runtime).asFunction (runtime).callWithThis (
@@ -964,7 +983,8 @@ bool invokeEnginePrototypeSetter(Runtime& runtime, const std::string& property,
964983 descriptorValue.asObject (runtime).getProperty (runtime, " set" );
965984 if (setterValue.isObject () &&
966985 setterValue.asObject (runtime).isFunction (runtime)) {
967- Value thisValue = bridge_->findRoundTripValue (runtime, object_);
986+ Value thisValue = bridge_->findRoundTripValue (runtime, object_,
987+ nullptr , true );
968988 if (thisValue.isObject ()) {
969989 Value args[] = {Value (runtime, value)};
970990 setterValue.asObject (runtime).asFunction (runtime).callWithThis (
@@ -1145,7 +1165,7 @@ Value get(Runtime& runtime, const PropNameID& name) override {
11451165 return self->callObjectSelector (runtime, selectorName, nullptr ,
11461166 args + 1 , count - 1 );
11471167 }
1148- return callObjCSelector (runtime, bridge, object, false , selectorName,
1168+ return callObjCSelector (runtime, bridge, object, false , selectorName,
11491169 nullptr , args + 1 , count - 1 );
11501170 });
11511171 }
@@ -1162,19 +1182,30 @@ Value get(Runtime& runtime, const PropNameID& name) override {
11621182 }
11631183
11641184 id object = self->object_ ;
1185+ bool ownsObject = self->ownsObject_ ;
11651186 if (self->bridge_ != nullptr ) {
11661187 self->bridge_ ->forgetRoundTripValue (runtime, object);
1167- }
1168- if (self->ownsObject_ ) {
1169- [object release ];
1188+ self->bridge_ ->forgetObjectExpandos (object);
11701189 }
11711190 self->object_ = nil ;
11721191 self->ownsObject_ = false ;
11731192 if (self->lifetimeState_ != nullptr ) {
11741193 self->lifetimeState_ ->clear ();
11751194 }
11761195 self->consumed_ = true ;
1177- return makeNativeObjectValue (runtime, self->bridge_ , object, retained);
1196+ try {
1197+ Value result =
1198+ makeNativeObjectValue (runtime, self->bridge_ , object, retained);
1199+ if (!retained && ownsObject) {
1200+ [object release ];
1201+ }
1202+ return result;
1203+ } catch (...) {
1204+ if (!retained && ownsObject) {
1205+ [object release ];
1206+ }
1207+ throw ;
1208+ }
11781209 });
11791210 }
11801211 if (property == " toString" ) {
@@ -1183,7 +1214,7 @@ Value get(Runtime& runtime, const PropNameID& name) override {
11831214 runtime, PropNameID::forAscii (runtime, " toString" ), 0 ,
11841215 [object](Runtime& runtime, const Value&, const Value*, size_t ) -> Value {
11851216 return NativeApiObjectHostObject::descriptionString (runtime, object);
1186- });
1217+ });
11871218 }
11881219 if (property == " description" ) {
11891220 return descriptionString (runtime, object_);
@@ -1611,16 +1642,38 @@ throw JSError(
16111642 } else if (args[0 ].isObject ()) {
16121643 Object object = args[0 ].asObject (runtime);
16131644 if (object.isHostObject <NativeApiPointerHostObject>(runtime)) {
1614- pointer = object
1615- .getHostObject <NativeApiPointerHostObject>(
1616- runtime)
1617- ->pointer ();
1645+ auto pointerHost =
1646+ object.getHostObject <NativeApiPointerHostObject>(
1647+ runtime);
1648+ pointer = pointerHost->pointer ();
1649+ if (pointerHost->backingValue () != nullptr ) {
1650+ Value backingValue (runtime, *pointerHost->backingValue ());
1651+ id backingObject =
1652+ NativeApiObjectHostObject::nativeObjectFromValue (
1653+ runtime, backingValue);
1654+ if (backingObject == static_cast <id >(pointer) &&
1655+ backingObject != nil &&
1656+ [backingObject isKindOfClass: cls]) {
1657+ return backingValue;
1658+ }
1659+ }
16181660 } else if (object.isHostObject <NativeApiReferenceHostObject>(
16191661 runtime)) {
1620- pointer = object
1621- .getHostObject <NativeApiReferenceHostObject>(
1622- runtime)
1623- ->data ();
1662+ auto referenceHost =
1663+ object.getHostObject <NativeApiReferenceHostObject>(
1664+ runtime);
1665+ pointer = referenceHost->data ();
1666+ if (referenceHost->backingValue () != nullptr ) {
1667+ Value backingValue (runtime, *referenceHost->backingValue ());
1668+ id backingObject =
1669+ NativeApiObjectHostObject::nativeObjectFromValue (
1670+ runtime, backingValue);
1671+ if (backingObject == static_cast <id >(pointer) &&
1672+ backingObject != nil &&
1673+ [backingObject isKindOfClass: cls]) {
1674+ return backingValue;
1675+ }
1676+ }
16241677 } else if (object.isHostObject <NativeApiObjectHostObject>(
16251678 runtime)) {
16261679 pointer = object
@@ -1782,15 +1835,15 @@ Value makeNativeObjectValue(Runtime& runtime,
17821835 return Value::null ();
17831836 }
17841837
1785- Value cached = bridge->findRoundTripValue (runtime, object);
1838+ Value cached = bridge->findRoundTripValue (runtime, object, nullptr , true );
17861839 if (!cached.isUndefined ()) {
17871840 // A consumed wrapper (e.g. an alloc'd placeholder singleton already passed
17881841 // to an initializer) must not be reused: drop the stale entry and re-wrap.
17891842 auto cachedHost =
17901843 cached.isObject ()
17911844 ? cached.asObject (runtime).getHostObject <NativeApiObjectHostObject>(runtime)
17921845 : nullptr ;
1793- if (cachedHost == nullptr || cachedHost->object () != nil ) {
1846+ if (cachedHost != nullptr && cachedHost->object () != nil ) {
17941847 if (ownsObject) {
17951848 [object release ];
17961849 }
@@ -1816,7 +1869,7 @@ Value makeNativeObjectValue(Runtime& runtime,
18161869 Object prototype = prototypeValue.asObject (runtime);
18171870 SetNativeApiObjectPrototype (runtime, result, prototype);
18181871 }
1819- bridge->rememberRoundTripValue (
1872+ bridge->rememberScopedRoundTripValue (
18201873 runtime, object, Value (runtime, result),
18211874 nativeObjectIsStringLike (object));
18221875 return result;
0 commit comments