88#include " ObjCBridge.h"
99#include " TypeConv.h"
1010#include " Util.h"
11+ #include " ffi/Class.h"
1112#include " ffi/NativeScriptException.h"
1213#include " js_native_api.h"
1314#include " js_native_api_types.h"
@@ -35,7 +36,8 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) {
3536}
3637
3738void ObjCClassMember::defineMembers (napi_env env, ObjCClassMemberMap& memberMap,
38- MDSectionOffset offset, napi_value constructor) {
39+ MDSectionOffset offset, napi_value constructor,
40+ ObjCClass* cls) {
3941 auto bridgeState = ObjCBridgeState::InstanceData (env);
4042
4143 napi_value prototype;
@@ -115,7 +117,7 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) {
115117
116118 bool hasProperty = false ;
117119 napi_has_named_property (env, jsObject, name.c_str (), &hasProperty);
118- if (hasProperty) {
120+ if (hasProperty && name != " init " ) {
119121 continue ;
120122 }
121123
@@ -135,6 +137,10 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) {
135137 .data = &kv.first ->second ,
136138 };
137139
140+ if ((flags & mdMemberIsInit) != 0 ) {
141+ kv.first ->second .cls = cls;
142+ }
143+
138144 if (name == " alloc" ) {
139145 property.method = JS_NSObject_alloc;
140146 }
@@ -198,6 +204,144 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, void** avalues, void
198204 return true ;
199205}
200206
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+
201345inline id assertSelf (napi_env env, napi_value jsThis) {
202346 id self;
203347 napi_unwrap (env, jsThis, (void **)&self);
@@ -216,21 +360,37 @@ inline id assertSelf(napi_env env, napi_value jsThis) {
216360 napi_value jsThis;
217361 ObjCClassMember* method;
218362
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);
220365
221366 id self = assertSelf (env, jsThis);
222367
223368 if (self == nullptr ) {
224369 return nullptr ;
225370 }
226371
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+
227387 Cif* cif = method->cif ;
228388 if (cif == nullptr ) {
229389 cif = method->cif =
230390 method->bridgeState ->getMethodCif (env, method->methodOrGetter .signatureOffset );
231391 }
232392
233- size_t argc = cif->argc ;
393+ argc = cif->argc ;
234394 napi_get_cb_info (env, cbinfo, &argc, cif->argv , &jsThis, nullptr );
235395
236396 void * avalues[cif->cif.nargs];
0 commit comments