1
1
#include " Object.h"
2
+ #include " JSObject.h"
2
3
#include " ObjCBridge.h"
3
4
#include " js_native_api.h"
4
5
#include " node_api_util.h"
5
6
6
7
#import < Foundation/Foundation.h>
7
8
#include < objc/runtime.h>
8
9
10
+ static SEL JSWrapperObjectAssociationKey = @selector (JSWrapperObjectAssociationKey );
11
+
12
+ @interface JSWrapperObjectAssociation : NSObject
13
+
14
+ @property (nonatomic ) napi_env env;
15
+ @property (nonatomic ) napi_ref ref;
16
+
17
+ + (void )transferOwnership : (napi_env)env of : (napi_value)value toNative : (id )object ;
18
+
19
+ + (instancetype )associationFor : (id )object ;
20
+
21
+ - (instancetype )initWithEnv : (napi_env)env ref : (napi_ref)ref ;
22
+
23
+ @end
24
+
25
+ @implementation JSWrapperObjectAssociation
26
+
27
+ - (instancetype )initWithEnv : (napi_env)env ref : (napi_ref)ref {
28
+ self = [super init ];
29
+ if (self) {
30
+ self.env = env;
31
+ self.ref = ref;
32
+ }
33
+ return self;
34
+ }
35
+
36
+ + (void )transferOwnership : (napi_env)env of : (napi_value)value toNative : (id )object {
37
+ napi_ref ref = objc_bridge::make_ref (env, value);
38
+ JSWrapperObjectAssociation *association = [[JSWrapperObjectAssociation alloc ] initWithEnv: env ref: ref];
39
+ objc_setAssociatedObject (object, JSWrapperObjectAssociationKey, association, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
40
+ }
41
+
42
+ + (instancetype )associationFor : (id )object {
43
+ return objc_getAssociatedObject (object, JSWrapperObjectAssociationKey);
44
+ }
45
+
46
+ - (void )dealloc {
47
+ napi_delete_reference (self.env , self.ref );
48
+ }
49
+
50
+ @end
51
+
52
+ napi_value JS_transferOwnershipToNative (napi_env env, napi_callback_info cbinfo) {
53
+ size_t argc = 1 ;
54
+ napi_value arg;
55
+ napi_get_cb_info (env, cbinfo, &argc, &arg, nullptr , nullptr );
56
+
57
+ id obj = nil ;
58
+ napi_unwrap (env, arg, (void **)&obj);
59
+
60
+ [JSWrapperObjectAssociation transferOwnership: env of: arg toNative: obj];
61
+ }
62
+
9
63
namespace objc_bridge {
10
64
11
65
const char *nativeObjectProxySource = R"(
12
- (function (object, isArray) {
66
+ (function (object, isArray, transferOwnershipToNative) {
67
+ let isTransfered = false;
68
+
13
69
return new Proxy(object, {
14
70
get (target, name) {
15
71
if (name in target) {
22
78
return target.objectAtIndex(index);
23
79
}
24
80
}
25
-
26
- // return target[name];
27
81
},
28
82
29
- // set (target, name, value) {
30
- // if (name in target) {
31
- // target[name] = value;
32
- // return true;
33
- // }
34
-
35
- // // if (isArray) {
36
- // // const index = Number(name);
37
- // // if (!isNaN(index)) {
38
- // // target.setObjectAtIndexedSubscript(value, index);
39
- // // return true;
40
- // // }
41
- // // }
42
-
43
- // if (!target.__customProps__) {
44
- // target.__customProps__ = {};
45
- // }
46
-
47
- // target.__customProps__[name] = value;
48
- // return true;
49
- // },
83
+ set (target, name, value) {
84
+ if (isArray) {
85
+ const index = Number(name);
86
+ if (!isNaN(index)) {
87
+ target.setObjectAtIndexedSubscript(value, index);
88
+ return true;
89
+ }
90
+ }
91
+
92
+ if (!(name in target) && !isTransfered) {
93
+ isTransfered = true;
94
+ transferOwnershipToNative(target);
95
+ }
96
+
97
+ target[name] = value;
98
+
99
+ return true;
100
+ },
50
101
});
51
102
})
52
103
)" ;
@@ -57,6 +108,10 @@ void initProxyFactory(napi_env env, ObjCBridgeState *state) {
57
108
&script);
58
109
napi_run_script (env, script, &result);
59
110
state->createNativeProxy = make_ref (env, result);
111
+
112
+ napi_value transferOwnershipToNative;
113
+ napi_create_function (env, " transferOwnershipToNative" , NAPI_AUTO_LENGTH, JS_transferOwnershipToNative, nullptr , &transferOwnershipToNative);
114
+ state->transferOwnershipToNative = make_ref (env, transferOwnershipToNative);
60
115
}
61
116
62
117
void finalize_objc_object (napi_env /* env*/ , void *data, void *hint) {
@@ -84,6 +139,13 @@ void finalize_objc_object(napi_env /*env*/, void *data, void *hint) {
84
139
unregisterObject (obj);
85
140
}
86
141
142
+ JSWrapperObjectAssociation *association = [JSWrapperObjectAssociation associationFor: obj];
143
+ if (association != nil ) {
144
+ napi_value jsObject = get_ref_value (env, association.ref );
145
+ [obj retain ];
146
+ return proxyNativeObject (env, jsObject, obj);
147
+ }
148
+
87
149
napi_value result = nil ;
88
150
89
151
Class cls = object_getClass (obj);
@@ -114,25 +176,12 @@ void finalize_objc_object(napi_env /*env*/, void *data, void *hint) {
114
176
115
177
napi_value orig = result;
116
178
117
- result =
118
- proxyNativeObject (env, result, [obj isKindOfClass: [NSArray class ]]);
119
-
120
- // We need to wrap the proxied object separately except for Hermes,
121
- // We'll just ignore the error there.
122
- napi_wrap (env, result, obj, nullptr , nullptr , nullptr );
123
-
124
- napi_ref ref = nullptr ;
125
- NAPI_GUARD (napi_add_finalizer (env, result, obj, finalize_objc_object, this ,
126
- &ref)) {
127
- NAPI_THROW_LAST_ERROR
128
- return nullptr ;
129
- }
130
-
131
- objectRefs[obj] = ref;
132
-
133
179
if (ownership == kUnownedObject ) {
134
180
[obj retain ];
135
181
}
182
+
183
+ result = proxyNativeObject (env, result, obj);
184
+
136
185
// #if DEBUG
137
186
// napi_value global, Error, error, stack;
138
187
// napi_get_global(env, &global);
0 commit comments