8
8
#include " ObjCBridge.h"
9
9
#include " TypeConv.h"
10
10
#include " Util.h"
11
+ #include " ffi/Class.h"
11
12
#include " ffi/NativeScriptException.h"
12
13
#include " js_native_api.h"
13
14
#include " js_native_api_types.h"
@@ -35,7 +36,8 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) {
35
36
}
36
37
37
38
void ObjCClassMember::defineMembers (napi_env env, ObjCClassMemberMap& memberMap,
38
- MDSectionOffset offset, napi_value constructor) {
39
+ MDSectionOffset offset, napi_value constructor,
40
+ ObjCClass* cls) {
39
41
auto bridgeState = ObjCBridgeState::InstanceData (env);
40
42
41
43
napi_value prototype;
@@ -115,7 +117,7 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) {
115
117
116
118
bool hasProperty = false ;
117
119
napi_has_named_property (env, jsObject, name.c_str (), &hasProperty);
118
- if (hasProperty) {
120
+ if (hasProperty && name != " init " ) {
119
121
continue ;
120
122
}
121
123
@@ -135,6 +137,10 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) {
135
137
.data = &kv.first ->second ,
136
138
};
137
139
140
+ if ((flags & mdMemberIsInit) != 0 ) {
141
+ kv.first ->second .cls = cls;
142
+ }
143
+
138
144
if (name == " alloc" ) {
139
145
property.method = JS_NSObject_alloc;
140
146
}
@@ -198,6 +204,144 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, void** avalues, void
198
204
return true ;
199
205
}
200
206
207
+ // Utility function to check if a JS value can be converted to a specific type
208
+ bool canConvertToType (napi_env env, napi_value value, std::shared_ptr<TypeConv> typeConv) {
209
+ if (value == nullptr ) {
210
+ return true ; // null/undefined can convert to most types
211
+ }
212
+
213
+ napi_valuetype jsType;
214
+ napi_typeof (env, value, &jsType);
215
+
216
+ if (jsType == napi_null || jsType == napi_undefined) {
217
+ return true ; // null/undefined are generally acceptable
218
+ }
219
+
220
+ // Check basic type compatibility based on TypeConv kind
221
+ switch (typeConv->kind ) {
222
+ case mdTypeBool:
223
+ return jsType == napi_boolean || jsType == napi_number;
224
+
225
+ case mdTypeChar:
226
+ case mdTypeUChar:
227
+ case mdTypeSShort:
228
+ case mdTypeUShort:
229
+ case mdTypeSInt:
230
+ case mdTypeUInt:
231
+ case mdTypeSLong:
232
+ case mdTypeULong:
233
+ case mdTypeSInt64:
234
+ case mdTypeUInt64:
235
+ case mdTypeFloat:
236
+ case mdTypeDouble:
237
+ return jsType == napi_number || jsType == napi_bigint;
238
+
239
+ case mdTypeInstanceObject:
240
+ case mdTypeNSStringObject:
241
+ case mdTypeNSMutableStringObject: {
242
+ if (jsType == napi_string) {
243
+ // String can convert to NSString
244
+ return true ;
245
+ }
246
+ if (jsType == napi_object) {
247
+ bool isArray;
248
+ napi_is_array (env, value, &isArray);
249
+ if (isArray) {
250
+ // Array can convert to NSArray
251
+ return true ;
252
+ }
253
+ // Check if it's a wrapped native object
254
+ void * wrapped;
255
+ napi_status status = napi_unwrap (env, value, &wrapped);
256
+ return status == napi_ok;
257
+ }
258
+ return false ;
259
+ }
260
+
261
+ case mdTypeSelector:
262
+ return jsType == napi_string;
263
+
264
+ case mdTypePointer:
265
+ case mdTypeOpaquePointer:
266
+ return jsType == napi_object || jsType == napi_bigint || jsType == napi_string;
267
+
268
+ case mdTypeStruct:
269
+ return jsType == napi_object;
270
+
271
+ case mdTypeBlock:
272
+ return jsType == napi_function;
273
+
274
+ default :
275
+ return true ; // For unknown types, assume compatible
276
+ }
277
+ }
278
+
279
+ // Find the best initializer for a class given JS arguments
280
+ ObjCClassMember* findInitializerForArgs (napi_env env, ObjCClassMemberMap* initializers, size_t argc,
281
+ napi_value* argv) {
282
+ std::vector<ObjCClassMember*> candidates;
283
+
284
+ // First pass: find initializers with matching argument count
285
+ for (auto & pair : *initializers) {
286
+ auto * candidate = &pair.second ;
287
+ const char * name = sel_getName (candidate->methodOrGetter .selector );
288
+ // NSLog(@"Checking initializer: %s", name);
289
+ if (name[0 ] != ' i' || name[1 ] != ' n' || name[2 ] != ' i' || name[3 ] != ' t' ) {
290
+ continue ;
291
+ }
292
+ Cif* cif = candidate->cif ;
293
+ if (!cif) {
294
+ // Need to get the CIF to check argument count
295
+ cif = const_cast <ObjCClassMember*>(candidate)->bridgeState ->getMethodCif (
296
+ env, candidate->methodOrGetter .signatureOffset );
297
+ }
298
+
299
+ // Match argument count (cif->argc excludes self and selector)
300
+ if (cif->argc == argc) {
301
+ bool canInvoke = true ;
302
+
303
+ // Check if all arguments can be converted to the expected types
304
+ for (size_t i = 0 ; i < argc; ++i) {
305
+ if (!canConvertToType (env, argv[i], cif->argTypes [i])) {
306
+ canInvoke = false ;
307
+ break ;
308
+ }
309
+ }
310
+
311
+ if (canInvoke) {
312
+ candidates.push_back (candidate);
313
+ }
314
+ }
315
+ }
316
+
317
+ if (candidates.empty ()) {
318
+ napi_throw_error (env, " NativeScriptException" ,
319
+ " No initializer found that matches constructor invocation." );
320
+ return nullptr ;
321
+ } else if (candidates.size () > 1 ) {
322
+ // Prefer "init" if no arguments
323
+ if (argc == 0 ) {
324
+ for (auto * candidate : candidates) {
325
+ const char * selectorName = sel_getName (candidate->methodOrGetter .selector );
326
+ if (strcmp (selectorName, " init" ) == 0 ) {
327
+ return candidate;
328
+ }
329
+ }
330
+ }
331
+
332
+ // If multiple candidates, throw an error with details
333
+ std::string errorMsg = " More than one initializer found that matches constructor invocation:" ;
334
+ for (const auto * candidate : candidates) {
335
+ errorMsg += " " ;
336
+ errorMsg += sel_getName (candidate->methodOrGetter .selector );
337
+ }
338
+ napi_throw_error (env, " NativeScriptException" , errorMsg.c_str ());
339
+ return nullptr ;
340
+ }
341
+
342
+ return candidates[0 ];
343
+ }
344
+
201
345
inline id assertSelf (napi_env env, napi_value jsThis) {
202
346
id self;
203
347
napi_unwrap (env, jsThis, (void **)&self);
@@ -216,21 +360,37 @@ inline id assertSelf(napi_env env, napi_value jsThis) {
216
360
napi_value jsThis;
217
361
ObjCClassMember* method;
218
362
219
- napi_get_cb_info (env, cbinfo, nullptr , nullptr , &jsThis, (void **)&method);
363
+ size_t argc = 0 ;
364
+ napi_get_cb_info (env, cbinfo, &argc, nullptr , &jsThis, (void **)&method);
220
365
221
366
id self = assertSelf (env, jsThis);
222
367
223
368
if (self == nullptr ) {
224
369
return nullptr ;
225
370
}
226
371
372
+ SEL sel = method->methodOrGetter .selector ;
373
+ if (sel == @selector (init ) && argc > 0 ) {
374
+ napi_value argv[argc];
375
+ napi_get_cb_info (env, cbinfo, &argc, argv, &jsThis, nullptr );
376
+ Class nativeClass = [self class ];
377
+ ObjCBridgeState* state = ObjCBridgeState::InstanceData (env);
378
+ ObjCClass* cls = state->classesByPointer [nativeClass];
379
+ // NSLog(@"find init for class: %@, cls: %p", nativeClass, cls);
380
+ ObjCClassMember* newMethod = findInitializerForArgs (env, &cls->members , argc, argv);
381
+ // NSLog(@"new init: %p", newMethod);
382
+ if (newMethod != nullptr ) {
383
+ method = newMethod;
384
+ }
385
+ }
386
+
227
387
Cif* cif = method->cif ;
228
388
if (cif == nullptr ) {
229
389
cif = method->cif =
230
390
method->bridgeState ->getMethodCif (env, method->methodOrGetter .signatureOffset );
231
391
}
232
392
233
- size_t argc = cif->argc ;
393
+ argc = cif->argc ;
234
394
napi_get_cb_info (env, cbinfo, &argc, cif->argv , &jsThis, nullptr );
235
395
236
396
void * avalues[cif->cif.nargs];
0 commit comments