diff --git a/NativeScript/runtime/AOTDirectCalls.h b/NativeScript/runtime/AOTDirectCalls.h new file mode 100644 index 00000000..8d8d6937 --- /dev/null +++ b/NativeScript/runtime/AOTDirectCalls.h @@ -0,0 +1,19 @@ +// AUTO-GENERATED by scripts/generate-aot.py — do not edit manually. +#ifndef AOTDirectCalls_h +#define AOTDirectCalls_h + +#include "Common.h" +#include "Metadata.h" + +namespace tns { + +v8::FunctionCallback GetAOTDirectCall(const char* className, + const char* selectorName); + +typedef void* AOTBlockInvokeFunc; +AOTBlockInvokeFunc GetAOTBlockInvoke(const TypeEncoding* typeEncoding, + int argsCount); + +} // namespace tns + +#endif /* AOTDirectCalls_h */ diff --git a/NativeScript/runtime/AOTDirectCalls.mm b/NativeScript/runtime/AOTDirectCalls.mm new file mode 100644 index 00000000..b9098805 --- /dev/null +++ b/NativeScript/runtime/AOTDirectCalls.mm @@ -0,0 +1,1015 @@ +// AUTO-GENERATED by scripts/generate-aot.py — do not edit manually. +#include "AOTDirectCalls.h" +#include +#include +#include "ArgConverter.h" +#include "Caches.h" +#include "Helpers.h" +#include "Interop.h" +#include "NativeScriptException.h" + +using namespace v8; + +namespace tns { + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +static inline id AOTExtractTarget(Isolate* isolate, Local receiver, bool& callSuper) { + BaseDataWrapper* wrapper = tns::GetValue(isolate, receiver); + if (wrapper == nullptr) { + callSuper = false; + return nil; + } + + id target = nil; + callSuper = false; + + if (wrapper->Type() == WrapperType::ObjCAllocObject) { + ObjCAllocDataWrapper* allocWrapper = static_cast(wrapper); + target = [allocWrapper->Klass() alloc]; + } else if (wrapper->Type() == WrapperType::ObjCObject) { + ObjCDataWrapper* objcWrapper = static_cast(wrapper); + target = objcWrapper->Data(); + std::string className = object_getClassName(target); + auto cache = Caches::Get(isolate); + callSuper = cache->ClassPrototypes.find(className) != cache->ClassPrototypes.end(); + } + + return target; +} + +static inline Local AOTWrapId(Local context, id result) { + Isolate* isolate = context->GetIsolate(); + if (result == nil) return Null(isolate); + + if ([result isKindOfClass:[NSString class]]) { + return tns::ToV8String(isolate, [(NSString*)result UTF8String]); + } + if ([result isKindOfClass:[NSNull class]]) { + return Null(isolate); + } + if ([result isKindOfClass:[NSNumber class]] && ![result isKindOfClass:[NSDecimalNumber class]]) { + return Number::New(isolate, [(NSNumber*)result doubleValue]); + } + + auto* wrapper = new ObjCDataWrapper(result); + Local jsResult = ArgConverter::ConvertArgument(context, wrapper); + tns::DeleteWrapperIfUnused(isolate, jsResult, wrapper); + return jsResult; +} + +// --------------------------------------------------------------------------- +// Per-method stubs (18 methods) +// --------------------------------------------------------------------------- + +// NSObject -[respondsToSelector:] +static void AOT_NSObject_respondsToSelector(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + SEL arg0 = sel_registerName(tns::ToString(isolate, info[0]).c_str()); + + BOOL result; + @try { + if (callSuper) { + objc_super sup = {target, [NSObject class]}; + result = ((BOOL (*)(objc_super*, SEL, SEL))objc_msgSendSuper)( + &sup, @selector(respondsToSelector:), arg0); + } else { + result = [(NSObject*)target respondsToSelector:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(v8::Boolean::New(isolate, result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSObject -[isKindOfClass:] +static void AOT_NSObject_isKindOfClass(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + Class arg0 = nil; + BaseDataWrapper* argW0 = tns::GetValue(isolate, info[0]); + if (argW0 != nullptr && argW0->Type() == WrapperType::ObjCClass) + arg0 = static_cast(argW0)->Klass(); + else if (tns::IsString(info[0])) + arg0 = objc_getClass(tns::ToString(isolate, info[0]).c_str()); + + BOOL result; + @try { + if (callSuper) { + objc_super sup = {target, [NSObject class]}; + result = ((BOOL (*)(objc_super*, SEL, Class))objc_msgSendSuper)( + &sup, @selector(isKindOfClass:), arg0); + } else { + result = [(NSObject*)target isKindOfClass:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(v8::Boolean::New(isolate, result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSObject -[isEqual:] +static void AOT_NSObject_isEqual(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + id arg0 = Interop::ToObject(context, info[0]); + + BOOL result; + @try { + if (callSuper) { + objc_super sup = {target, [NSObject class]}; + result = + ((BOOL (*)(objc_super*, SEL, id))objc_msgSendSuper)(&sup, @selector(isEqual:), arg0); + } else { + result = [(NSObject*)target isEqual:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(v8::Boolean::New(isolate, result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSObject -[description] +static void AOT_NSObject_description(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + + id result; + @try { + if (callSuper) { + objc_super sup = {target, [NSObject class]}; + result = ((id (*)(objc_super*, SEL))objc_msgSendSuper)(&sup, @selector(description)); + } else { + result = [(NSObject*)target description]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(AOTWrapId(context, result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSObject -[hash] +static void AOT_NSObject_hash(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + + unsigned long result; + @try { + if (callSuper) { + objc_super sup = {target, [NSObject class]}; + result = ((unsigned long (*)(objc_super*, SEL))objc_msgSendSuper)(&sup, @selector(hash)); + } else { + result = [(NSObject*)target hash]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(Number::New(isolate, (double)result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableArray -[removeAllObjects] +static void AOT_NSMutableArray_removeAllObjects(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableArray class]}; + ((void (*)(objc_super*, SEL))objc_msgSendSuper)(&sup, @selector(removeAllObjects)); + } else { + [(NSMutableArray*)target removeAllObjects]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableArray -[addObject:] +static void AOT_NSMutableArray_addObject(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + id arg0 = Interop::ToObject(context, info[0]); + + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableArray class]}; + ((void (*)(objc_super*, SEL, id))objc_msgSendSuper)(&sup, @selector(addObject:), arg0); + } else { + [(NSMutableArray*)target addObject:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableArray -[objectAtIndex:] +static void AOT_NSMutableArray_objectAtIndex(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + unsigned long arg0 = (unsigned long)tns::ToNumber(isolate, info[0]); + + id result; + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableArray class]}; + result = ((id (*)(objc_super*, SEL, unsigned long))objc_msgSendSuper)( + &sup, @selector(objectAtIndex:), arg0); + } else { + result = [(NSMutableArray*)target objectAtIndex:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(AOTWrapId(context, result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableArray -[count] +static void AOT_NSMutableArray_count(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + + unsigned long result; + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableArray class]}; + result = ((unsigned long (*)(objc_super*, SEL))objc_msgSendSuper)(&sup, @selector(count)); + } else { + result = [(NSMutableArray*)target count]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(Number::New(isolate, (double)result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableArray -[insertObject:atIndex:] +static void AOT_NSMutableArray_insertObject_atIndex(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + id arg0 = Interop::ToObject(context, info[0]); + unsigned long arg1 = (unsigned long)tns::ToNumber(isolate, info[1]); + + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableArray class]}; + ((void (*)(objc_super*, SEL, id, unsigned long))objc_msgSendSuper)( + &sup, @selector(insertObject:atIndex:), arg0, arg1); + } else { + [(NSMutableArray*)target insertObject:arg0 atIndex:arg1]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableArray -[removeObjectAtIndex:] +static void AOT_NSMutableArray_removeObjectAtIndex(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + unsigned long arg0 = (unsigned long)tns::ToNumber(isolate, info[0]); + + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableArray class]}; + ((void (*)(objc_super*, SEL, unsigned long))objc_msgSendSuper)( + &sup, @selector(removeObjectAtIndex:), arg0); + } else { + [(NSMutableArray*)target removeObjectAtIndex:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSArray -[objectAtIndex:] +static void AOT_NSArray_objectAtIndex(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + unsigned long arg0 = (unsigned long)tns::ToNumber(isolate, info[0]); + + id result; + @try { + if (callSuper) { + objc_super sup = {target, [NSArray class]}; + result = ((id (*)(objc_super*, SEL, unsigned long))objc_msgSendSuper)( + &sup, @selector(objectAtIndex:), arg0); + } else { + result = [(NSArray*)target objectAtIndex:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(AOTWrapId(context, result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSArray -[count] +static void AOT_NSArray_count(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + + unsigned long result; + @try { + if (callSuper) { + objc_super sup = {target, [NSArray class]}; + result = ((unsigned long (*)(objc_super*, SEL))objc_msgSendSuper)(&sup, @selector(count)); + } else { + result = [(NSArray*)target count]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(Number::New(isolate, (double)result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSDictionary -[objectForKeyedSubscript:] +static void AOT_NSDictionary_objectForKeyedSubscript(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + id arg0 = Interop::ToObject(context, info[0]); + + id result; + @try { + if (callSuper) { + objc_super sup = {target, [NSDictionary class]}; + result = ((id (*)(objc_super*, SEL, id))objc_msgSendSuper)( + &sup, @selector(objectForKeyedSubscript:), arg0); + } else { + result = [(NSDictionary*)target objectForKeyedSubscript:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(AOTWrapId(context, result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSDictionary -[count] +static void AOT_NSDictionary_count(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + + unsigned long result; + @try { + if (callSuper) { + objc_super sup = {target, [NSDictionary class]}; + result = ((unsigned long (*)(objc_super*, SEL))objc_msgSendSuper)(&sup, @selector(count)); + } else { + result = [(NSDictionary*)target count]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(Number::New(isolate, (double)result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableDictionary -[setObject:forKey:] +static void AOT_NSMutableDictionary_setObject_forKey(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + id arg0 = Interop::ToObject(context, info[0]); + id arg1 = Interop::ToObject(context, info[1]); + + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableDictionary class]}; + ((void (*)(objc_super*, SEL, id, id))objc_msgSendSuper)(&sup, @selector(setObject:forKey:), + arg0, arg1); + } else { + [(NSMutableDictionary*)target setObject:arg0 forKey:arg1]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSMutableDictionary -[removeObjectForKey:] +static void AOT_NSMutableDictionary_removeObjectForKey(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + id arg0 = Interop::ToObject(context, info[0]); + + @try { + if (callSuper) { + objc_super sup = {target, [NSMutableDictionary class]}; + ((void (*)(objc_super*, SEL, id))objc_msgSendSuper)(&sup, @selector(removeObjectForKey:), + arg0); + } else { + [(NSMutableDictionary*)target removeObjectForKey:arg0]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// NSString -[length] +static void AOT_NSString_length(const FunctionCallbackInfo& info) { + try { + Isolate* isolate = info.GetIsolate(); + bool callSuper; + id target = AOTExtractTarget(isolate, info.This(), callSuper); + if (target == nil) return; + + unsigned long result; + @try { + if (callSuper) { + objc_super sup = {target, [NSString class]}; + result = ((unsigned long (*)(objc_super*, SEL))objc_msgSendSuper)(&sup, @selector(length)); + } else { + result = [(NSString*)target length]; + } + } @catch (NSException* e) { + throw NativeScriptException([[e description] UTF8String]); + } + + info.GetReturnValue().Set(Number::New(isolate, (double)result)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(info.GetIsolate()); + } +} + +// --------------------------------------------------------------------------- +// Block invoke stubs (11 patterns) +// --------------------------------------------------------------------------- + +static void AOTBlockInvoke_void_noargs(void* _block) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 0, nullptr).ToLocal(&result); + (void)success; +} + +static void AOTBlockInvoke_void_id(void* _block, id nativeArg0) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[1]; + if (nativeArg0 == nil) { + jsArgs[0] = Null(isolate); + } else { + ObjCDataWrapper* w0 = new ObjCDataWrapper(nativeArg0); + jsArgs[0] = ArgConverter::ConvertArgument(context, w0); + tns::DeleteWrapperIfUnused(isolate, jsArgs[0], w0); + } + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 1, jsArgs).ToLocal(&result); + (void)success; +} + +static void AOTBlockInvoke_void_id_id(void* _block, id nativeArg0, id nativeArg1) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[2]; + if (nativeArg0 == nil) { + jsArgs[0] = Null(isolate); + } else { + ObjCDataWrapper* w0 = new ObjCDataWrapper(nativeArg0); + jsArgs[0] = ArgConverter::ConvertArgument(context, w0); + tns::DeleteWrapperIfUnused(isolate, jsArgs[0], w0); + } + if (nativeArg1 == nil) { + jsArgs[1] = Null(isolate); + } else { + ObjCDataWrapper* w1 = new ObjCDataWrapper(nativeArg1); + jsArgs[1] = ArgConverter::ConvertArgument(context, w1); + tns::DeleteWrapperIfUnused(isolate, jsArgs[1], w1); + } + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 2, jsArgs).ToLocal(&result); + (void)success; +} + +static void AOTBlockInvoke_void_BOOL(void* _block, BOOL nativeArg0) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[1]; + jsArgs[0] = v8::Boolean::New(isolate, nativeArg0); + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 1, jsArgs).ToLocal(&result); + (void)success; +} + +static void AOTBlockInvoke_void_long(void* _block, long nativeArg0) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[1]; + jsArgs[0] = Number::New(isolate, (double)nativeArg0); + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 1, jsArgs).ToLocal(&result); + (void)success; +} + +static void AOTBlockInvoke_void_ulong(void* _block, unsigned long nativeArg0) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[1]; + jsArgs[0] = Number::New(isolate, (double)nativeArg0); + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 1, jsArgs).ToLocal(&result); + (void)success; +} + +static BOOL AOTBlockInvoke_BOOL_noargs(void* _block) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return (BOOL)0; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 0, nullptr).ToLocal(&result); + + if (!success || result.IsEmpty() || result->IsNullOrUndefined()) { + return (BOOL)0; + } + return tns::ToBool(result); +} + +static BOOL AOTBlockInvoke_BOOL_id(void* _block, id nativeArg0) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return (BOOL)0; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[1]; + if (nativeArg0 == nil) { + jsArgs[0] = Null(isolate); + } else { + ObjCDataWrapper* w0 = new ObjCDataWrapper(nativeArg0); + jsArgs[0] = ArgConverter::ConvertArgument(context, w0); + tns::DeleteWrapperIfUnused(isolate, jsArgs[0], w0); + } + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 1, jsArgs).ToLocal(&result); + + if (!success || result.IsEmpty() || result->IsNullOrUndefined()) { + return (BOOL)0; + } + return tns::ToBool(result); +} + +static BOOL AOTBlockInvoke_BOOL_id_ulong_BOOL(void* _block, id nativeArg0, unsigned long nativeArg1, + BOOL nativeArg2) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return (BOOL)0; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[3]; + if (nativeArg0 == nil) { + jsArgs[0] = Null(isolate); + } else { + ObjCDataWrapper* w0 = new ObjCDataWrapper(nativeArg0); + jsArgs[0] = ArgConverter::ConvertArgument(context, w0); + tns::DeleteWrapperIfUnused(isolate, jsArgs[0], w0); + } + jsArgs[1] = Number::New(isolate, (double)nativeArg1); + jsArgs[2] = v8::Boolean::New(isolate, nativeArg2); + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 3, jsArgs).ToLocal(&result); + + if (!success || result.IsEmpty() || result->IsNullOrUndefined()) { + return (BOOL)0; + } + return tns::ToBool(result); +} + +static id AOTBlockInvoke_id_noargs(void* _block) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return nil; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 0, nullptr).ToLocal(&result); + + if (!success || result.IsEmpty() || result->IsNullOrUndefined()) { + return nil; + } + return Interop::ToObject(context, result); +} + +static id AOTBlockInvoke_id_id(void* _block, id nativeArg0) { + Interop::JSBlock* block = static_cast(_block); + MethodCallbackWrapper* wrapper = static_cast(block->userData); + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + + if (!wrapper->isolateWrapper_.IsValid()) { + return nil; + } + + v8::Locker locker(isolate); + Isolate::Scope isolateScope(isolate); + HandleScope handleScope(isolate); + auto cache = Caches::Get(isolate); + Local context = cache->GetContext(); + Context::Scope contextScope(context); + + Local jsArgs[1]; + if (nativeArg0 == nil) { + jsArgs[0] = Null(isolate); + } else { + ObjCDataWrapper* w0 = new ObjCDataWrapper(nativeArg0); + jsArgs[0] = ArgConverter::ConvertArgument(context, w0); + tns::DeleteWrapperIfUnused(isolate, jsArgs[0], w0); + } + + Local callback = wrapper->callback_->Get(isolate).As(); + Local result; + bool success = callback->Call(context, context->Global(), 1, jsArgs).ToLocal(&result); + + if (!success || result.IsEmpty() || result->IsNullOrUndefined()) { + return nil; + } + return Interop::ToObject(context, result); +} + +// --------------------------------------------------------------------------- +// Matchers +// --------------------------------------------------------------------------- + +v8::FunctionCallback GetAOTDirectCall(const char* className, const char* selectorName) { + if (strcmp(className, "NSArray") == 0) { + if (strcmp(selectorName, "objectAtIndex:") == 0) return AOT_NSArray_objectAtIndex; + if (strcmp(selectorName, "count") == 0) return AOT_NSArray_count; + } + if (strcmp(className, "NSDictionary") == 0) { + if (strcmp(selectorName, "objectForKeyedSubscript:") == 0) + return AOT_NSDictionary_objectForKeyedSubscript; + if (strcmp(selectorName, "count") == 0) return AOT_NSDictionary_count; + } + if (strcmp(className, "NSMutableArray") == 0) { + if (strcmp(selectorName, "removeAllObjects") == 0) return AOT_NSMutableArray_removeAllObjects; + if (strcmp(selectorName, "addObject:") == 0) return AOT_NSMutableArray_addObject; + if (strcmp(selectorName, "objectAtIndex:") == 0) return AOT_NSMutableArray_objectAtIndex; + if (strcmp(selectorName, "count") == 0) return AOT_NSMutableArray_count; + if (strcmp(selectorName, "insertObject:atIndex:") == 0) + return AOT_NSMutableArray_insertObject_atIndex; + if (strcmp(selectorName, "removeObjectAtIndex:") == 0) + return AOT_NSMutableArray_removeObjectAtIndex; + } + if (strcmp(className, "NSMutableDictionary") == 0) { + if (strcmp(selectorName, "setObject:forKey:") == 0) + return AOT_NSMutableDictionary_setObject_forKey; + if (strcmp(selectorName, "removeObjectForKey:") == 0) + return AOT_NSMutableDictionary_removeObjectForKey; + } + if (strcmp(className, "NSObject") == 0) { + if (strcmp(selectorName, "respondsToSelector:") == 0) return AOT_NSObject_respondsToSelector; + if (strcmp(selectorName, "isKindOfClass:") == 0) return AOT_NSObject_isKindOfClass; + if (strcmp(selectorName, "isEqual:") == 0) return AOT_NSObject_isEqual; + if (strcmp(selectorName, "description") == 0) return AOT_NSObject_description; + if (strcmp(selectorName, "hash") == 0) return AOT_NSObject_hash; + } + if (strcmp(className, "NSString") == 0) { + if (strcmp(selectorName, "length") == 0) return AOT_NSString_length; + } + return nullptr; +} + +typedef void* AOTBlockInvokeFunc; + +AOTBlockInvokeFunc GetAOTBlockInvoke(const TypeEncoding* typeEncoding, int argsCount) { + BinaryTypeEncodingType retType = typeEncoding->type; + + if (argsCount == 0) { + if (retType == BinaryTypeEncodingType::VoidEncoding) + return (AOTBlockInvokeFunc)AOTBlockInvoke_void_noargs; + if (retType == BinaryTypeEncodingType::BoolEncoding) + return (AOTBlockInvokeFunc)AOTBlockInvoke_BOOL_noargs; + if ((retType == BinaryTypeEncodingType::IdEncoding || + retType == BinaryTypeEncodingType::InstanceTypeEncoding || + retType == BinaryTypeEncodingType::InterfaceDeclarationReference)) + return (AOTBlockInvokeFunc)AOTBlockInvoke_id_noargs; + return nullptr; + } + + if (argsCount == 1) { + const TypeEncoding* pEnc = typeEncoding; + pEnc = pEnc->next(); + BinaryTypeEncodingType p0Type = pEnc->type; + if (retType == BinaryTypeEncodingType::VoidEncoding && + (p0Type == BinaryTypeEncodingType::IdEncoding || + p0Type == BinaryTypeEncodingType::InstanceTypeEncoding || + p0Type == BinaryTypeEncodingType::InterfaceDeclarationReference)) + return (AOTBlockInvokeFunc)AOTBlockInvoke_void_id; + if (retType == BinaryTypeEncodingType::VoidEncoding && + p0Type == BinaryTypeEncodingType::BoolEncoding) + return (AOTBlockInvokeFunc)AOTBlockInvoke_void_BOOL; + if (retType == BinaryTypeEncodingType::VoidEncoding && + p0Type == BinaryTypeEncodingType::LongEncoding) + return (AOTBlockInvokeFunc)AOTBlockInvoke_void_long; + if (retType == BinaryTypeEncodingType::VoidEncoding && + p0Type == BinaryTypeEncodingType::ULongEncoding) + return (AOTBlockInvokeFunc)AOTBlockInvoke_void_ulong; + if (retType == BinaryTypeEncodingType::BoolEncoding && + (p0Type == BinaryTypeEncodingType::IdEncoding || + p0Type == BinaryTypeEncodingType::InstanceTypeEncoding || + p0Type == BinaryTypeEncodingType::InterfaceDeclarationReference)) + return (AOTBlockInvokeFunc)AOTBlockInvoke_BOOL_id; + if ((retType == BinaryTypeEncodingType::IdEncoding || + retType == BinaryTypeEncodingType::InstanceTypeEncoding || + retType == BinaryTypeEncodingType::InterfaceDeclarationReference) && + (p0Type == BinaryTypeEncodingType::IdEncoding || + p0Type == BinaryTypeEncodingType::InstanceTypeEncoding || + p0Type == BinaryTypeEncodingType::InterfaceDeclarationReference)) + return (AOTBlockInvokeFunc)AOTBlockInvoke_id_id; + return nullptr; + } + + if (argsCount == 2) { + const TypeEncoding* pEnc = typeEncoding; + pEnc = pEnc->next(); + BinaryTypeEncodingType p0Type = pEnc->type; + pEnc = pEnc->next(); + BinaryTypeEncodingType p1Type = pEnc->type; + if (retType == BinaryTypeEncodingType::VoidEncoding && + (p0Type == BinaryTypeEncodingType::IdEncoding || + p0Type == BinaryTypeEncodingType::InstanceTypeEncoding || + p0Type == BinaryTypeEncodingType::InterfaceDeclarationReference) && + (p1Type == BinaryTypeEncodingType::IdEncoding || + p1Type == BinaryTypeEncodingType::InstanceTypeEncoding || + p1Type == BinaryTypeEncodingType::InterfaceDeclarationReference)) + return (AOTBlockInvokeFunc)AOTBlockInvoke_void_id_id; + return nullptr; + } + + if (argsCount == 3) { + const TypeEncoding* pEnc = typeEncoding; + pEnc = pEnc->next(); + BinaryTypeEncodingType p0Type = pEnc->type; + pEnc = pEnc->next(); + BinaryTypeEncodingType p1Type = pEnc->type; + pEnc = pEnc->next(); + BinaryTypeEncodingType p2Type = pEnc->type; + if (retType == BinaryTypeEncodingType::BoolEncoding && + (p0Type == BinaryTypeEncodingType::IdEncoding || + p0Type == BinaryTypeEncodingType::InstanceTypeEncoding || + p0Type == BinaryTypeEncodingType::InterfaceDeclarationReference) && + p1Type == BinaryTypeEncodingType::ULongEncoding && + p2Type == BinaryTypeEncodingType::BoolEncoding) + return (AOTBlockInvokeFunc)AOTBlockInvoke_BOOL_id_ulong_BOOL; + return nullptr; + } + + return nullptr; +} + +} // namespace tns diff --git a/NativeScript/runtime/Interop.h b/NativeScript/runtime/Interop.h index 3980be17..a85ddef9 100644 --- a/NativeScript/runtime/Interop.h +++ b/NativeScript/runtime/Interop.h @@ -3,203 +3,219 @@ #include #include -#include "libffi.h" + #include "Common.h" -#include "Metadata.h" #include "DataWrapper.h" #include "FFICall.h" +#include "Metadata.h" +#include "libffi.h" namespace tns { -typedef void (*FFIMethodCallback)(ffi_cif* cif, void* retValue, void** argValues, void* userData); +typedef void (*FFIMethodCallback)(ffi_cif* cif, void* retValue, + void** argValues, void* userData); struct MethodCall { - MethodCall(v8::Local context, - bool isPrimitiveFunction, - void* functionPointer, - const TypeEncoding* typeEncoding, - V8Args& args, - id target, - Class clazz, - SEL selector, - bool callSuper, - MetaType metaType, - bool provideErrorOutParameter, - bool ownsReturnedObject, - bool returnsUnmanaged, - bool isInitializer) - : context_(context), - isPrimitiveFunction_(isPrimitiveFunction), - functionPointer_(functionPointer), - typeEncoding_(typeEncoding), - args_(args), - target_(target), - clazz_(clazz), - selector_(selector), - callSuper_(callSuper), - metaType_(metaType), - provideErrorOutParameter_(provideErrorOutParameter), - ownsReturnedObject_(ownsReturnedObject), - returnsUnmanaged_(returnsUnmanaged), - isInitializer_(isInitializer) { - } + MethodCall(v8::Local context, bool isPrimitiveFunction, + void* functionPointer, const TypeEncoding* typeEncoding, + V8Args& args, id target, Class clazz, SEL selector, bool callSuper, + MetaType metaType, bool provideErrorOutParameter, + bool ownsReturnedObject, bool returnsUnmanaged, bool isInitializer) + : context_(context), + isPrimitiveFunction_(isPrimitiveFunction), + functionPointer_(functionPointer), + typeEncoding_(typeEncoding), + args_(args), + target_(target), + clazz_(clazz), + selector_(selector), + callSuper_(callSuper), + metaType_(metaType), + provideErrorOutParameter_(provideErrorOutParameter), + ownsReturnedObject_(ownsReturnedObject), + returnsUnmanaged_(returnsUnmanaged), + isInitializer_(isInitializer) {} - v8::Local context_; - bool isPrimitiveFunction_; - void* functionPointer_; - const TypeEncoding* typeEncoding_; - V8Args& args_; - id target_; - Class clazz_; - SEL selector_; - bool callSuper_; - MetaType metaType_; - bool provideErrorOutParameter_; - bool ownsReturnedObject_; - bool returnsUnmanaged_; - bool isInitializer_; + v8::Local context_; + bool isPrimitiveFunction_; + void* functionPointer_; + const TypeEncoding* typeEncoding_; + V8Args& args_; + id target_; + Class clazz_; + SEL selector_; + bool callSuper_; + MetaType metaType_; + bool provideErrorOutParameter_; + bool ownsReturnedObject_; + bool returnsUnmanaged_; + bool isInitializer_; }; -struct CMethodCall: MethodCall { - CMethodCall( - v8::Local context, - void* functionPointer, - const TypeEncoding* typeEncoding, - V8Args& args, - bool ownsReturnedObject, - bool returnsUnmanaged) - : MethodCall( - context, - true, - functionPointer, - typeEncoding, - args, - nil, - nil, - nil, - false, - MetaType::Undefined, - false, - ownsReturnedObject, - returnsUnmanaged, - false) { - } +struct CMethodCall : MethodCall { + CMethodCall(v8::Local context, void* functionPointer, + const TypeEncoding* typeEncoding, V8Args& args, + bool ownsReturnedObject, bool returnsUnmanaged) + : MethodCall(context, true, functionPointer, typeEncoding, args, nil, nil, + nil, false, MetaType::Undefined, false, ownsReturnedObject, + returnsUnmanaged, false) {} }; -struct ObjCMethodCall: public MethodCall { - ObjCMethodCall( - v8::Local context, - const MethodMeta* meta, - id target, - Class clazz, - V8Args& args, - bool callSuper) - : MethodCall( - context, - false, - callSuper ? (void*)objc_msgSendSuper : (void*)objc_msgSend, - meta->encodings()->first(), - args, - target, - clazz, - meta->selector(), - callSuper, - meta->type(), - meta->hasErrorOutParameter() && args.Length() < meta->encodings()->count - 1, - meta->ownsReturnedCocoaObject(), - false, - meta->isInitializer()) { - } +struct ObjCMethodCall : public MethodCall { + ObjCMethodCall(v8::Local context, const MethodMeta* meta, + id target, Class clazz, V8Args& args, bool callSuper) + : MethodCall(context, false, + callSuper ? (void*)objc_msgSendSuper : (void*)objc_msgSend, + meta->encodings()->first(), args, target, clazz, + meta->selector(), callSuper, meta->type(), + meta->hasErrorOutParameter() && + args.Length() < meta->encodings()->count - 1, + meta->ownsReturnedCocoaObject(), false, + meta->isInitializer()) {} }; class Interop { -public: - static void RegisterInteropTypes(v8::Local context); - static IMP CreateMethod(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData); - static id CallInitializer(v8::Local context, const MethodMeta* methodMeta, id target, Class clazz, V8Args& args); - static v8::Local CallFunction(ObjCMethodCall& methodCall); - static v8::Local CallFunction(CMethodCall& methodCall); - static v8::Local GetResultByType(v8::Local context, BaseDataWrapper* typeWrapper, BaseCall* call, std::shared_ptr> parentStruct = nullptr); - static v8::Local GetResult(v8::Local context, const TypeEncoding* typeEncoding, BaseCall* call, bool marshalToPrimitive, std::shared_ptr> parentStruct = nullptr, bool isStructMember = false, bool ownsReturnedObject = false, bool returnsUnmanaged = false, bool isInitializer = false); - static void SetStructPropertyValue(v8::Local context, StructWrapper* wrapper, StructField field, v8::Local value); - static void InitializeStruct(v8::Local context, void* destBuffer, std::vector fields, v8::Local inititalizer); - static void WriteTypeValue(v8::Local context, BaseDataWrapper* typeWrapper, void* dest, v8::Local arg); - static void WriteValue(v8::Local context, const TypeEncoding* typeEncoding, void* dest, v8::Local arg); - static id ToObject(v8::Local context, v8::Local arg); - static v8::Local GetPrimitiveReturnType(v8::Local context, BinaryTypeEncodingType type, BaseCall* call); -private: - static void ExecuteWriteValueDebugValidationsIfInDebug(v8::Local context, const TypeEncoding* typeEncoding, void* dest, v8::Local arg); - static std::pair CreateMethodInternal(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData); - static CFTypeRef CreateBlock(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData); - template - static void SetStructValue(v8::Local value, void* destBuffer, ptrdiff_t position); - static void InitializeStruct(v8::Local context, void* destBuffer, std::vector fields, v8::Local inititalizer, ptrdiff_t& position); - static void RegisterInteropType(v8::Local context, v8::Local types, std::string name, PrimitiveDataWrapper* wrapper, bool autoDelete = true); - static void RegisterBufferFromDataFunction(v8::Local context, v8::Local interop); - static void RegisterStringFromCString(v8::Local context, v8::Local interop); - static void RegisterHandleOfFunction(v8::Local context, v8::Local interop); - static void RegisterAllocFunction(v8::Local context, v8::Local interop); - static void RegisterFreeFunction(v8::Local context, v8::Local interop); - static void RegisterAdoptFunction(v8::Local context, v8::Local interop); - static void RegisterSizeOfFunction(v8::Local context, v8::Local interop); - static void SetFFIParams(v8::Local context, const TypeEncoding* typeEncoding, FFICall* call, const int argsCount, const int initialParameterIndex, V8Args& args); - static bool isRefTypeEqual(const TypeEncoding* typeEncoding,const char* clazz); - static v8::Local ToArray(v8::Local object); - static v8::Local StructToValue(v8::Local context, void* result, StructInfo structInfo, std::shared_ptr> parentStruct); - static const TypeEncoding* CreateEncoding(BinaryTypeEncodingType type); - static v8::Local HandleOf(v8::Local context, v8::Local value); - static v8::Local CallFunctionInternal(MethodCall& methodCall); - static bool IsNumbericType(BinaryTypeEncodingType type); - static v8::Local GetInteropType(v8::Local context, BinaryTypeEncodingType type); - static std::vector GetAdditionalProtocols(const TypeEncoding* typeEncoding); - static SEL GetSwizzledMethodSelector(SEL selector); - - template - static inline void SetValue(void* dest, T value) { - if (std::is_same::value) { - memcpy(dest, &value, sizeof(SEL*)); - } else { - *static_cast(dest) = value; - } - } + public: + static void RegisterInteropTypes(v8::Local context); + static IMP CreateMethod(const uint8_t initialParamIndex, + const uint8_t argsCount, + const TypeEncoding* typeEncoding, + FFIMethodCallback callback, void* userData); + static id CallInitializer(v8::Local context, + const MethodMeta* methodMeta, id target, + Class clazz, V8Args& args); + static v8::Local CallFunction(ObjCMethodCall& methodCall); + static v8::Local CallFunction(CMethodCall& methodCall); + static v8::Local GetResultByType( + v8::Local context, BaseDataWrapper* typeWrapper, + BaseCall* call, + std::shared_ptr> parentStruct = nullptr); + static v8::Local GetResult( + v8::Local context, const TypeEncoding* typeEncoding, + BaseCall* call, bool marshalToPrimitive, + std::shared_ptr> parentStruct = nullptr, + bool isStructMember = false, bool ownsReturnedObject = false, + bool returnsUnmanaged = false, bool isInitializer = false); + static void SetStructPropertyValue(v8::Local context, + StructWrapper* wrapper, StructField field, + v8::Local value); + static void InitializeStruct(v8::Local context, void* destBuffer, + std::vector fields, + v8::Local inititalizer); + static void WriteTypeValue(v8::Local context, + BaseDataWrapper* typeWrapper, void* dest, + v8::Local arg); + static void WriteValue(v8::Local context, + const TypeEncoding* typeEncoding, void* dest, + v8::Local arg); + static id ToObject(v8::Local context, v8::Local arg); + static v8::Local GetPrimitiveReturnType( + v8::Local context, BinaryTypeEncodingType type, + BaseCall* call); + + typedef struct JSBlock { + typedef struct { + uintptr_t reserved; + uintptr_t size; + void (*copy)(struct JSBlock*, const struct JSBlock*); + void (*dispose)(struct JSBlock*); + } JSBlockDescriptor; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + }; + + void* isa; + volatile int32_t flags; + int32_t reserved; + const void* invoke; + JSBlockDescriptor* descriptor; + void* userData; + ffi_closure* ffiClosure; - template - static void SetNumericValue(void* dest, double value) { - if (value < std::numeric_limits::lowest()) { - Interop::SetValue(dest, std::numeric_limits::lowest()); - } else if (value > std::numeric_limits::max()) { - Interop::SetValue(dest, std::numeric_limits::max()); - } else { - Interop::SetValue(dest, (T)value); - } + static JSBlockDescriptor kJSBlockDescriptor; + } JSBlock; + + private: + static void ExecuteWriteValueDebugValidationsIfInDebug( + v8::Local context, const TypeEncoding* typeEncoding, + void* dest, v8::Local arg); + static std::pair CreateMethodInternal( + const uint8_t initialParamIndex, const uint8_t argsCount, + const TypeEncoding* typeEncoding, FFIMethodCallback callback, + void* userData); + static CFTypeRef CreateBlock(const uint8_t initialParamIndex, + const uint8_t argsCount, + const TypeEncoding* typeEncoding, + FFIMethodCallback callback, void* userData); + template + static void SetStructValue(v8::Local value, void* destBuffer, + ptrdiff_t position); + static void InitializeStruct(v8::Local context, void* destBuffer, + std::vector fields, + v8::Local inititalizer, + ptrdiff_t& position); + static void RegisterInteropType(v8::Local context, + v8::Local types, std::string name, + PrimitiveDataWrapper* wrapper, + bool autoDelete = true); + static void RegisterBufferFromDataFunction(v8::Local context, + v8::Local interop); + static void RegisterStringFromCString(v8::Local context, + v8::Local interop); + static void RegisterHandleOfFunction(v8::Local context, + v8::Local interop); + static void RegisterAllocFunction(v8::Local context, + v8::Local interop); + static void RegisterFreeFunction(v8::Local context, + v8::Local interop); + static void RegisterAdoptFunction(v8::Local context, + v8::Local interop); + static void RegisterSizeOfFunction(v8::Local context, + v8::Local interop); + static void SetFFIParams(v8::Local context, + const TypeEncoding* typeEncoding, FFICall* call, + const int argsCount, const int initialParameterIndex, + V8Args& args); + static bool isRefTypeEqual(const TypeEncoding* typeEncoding, + const char* clazz); + static v8::Local ToArray(v8::Local object); + static v8::Local StructToValue( + v8::Local context, void* result, StructInfo structInfo, + std::shared_ptr> parentStruct); + static const TypeEncoding* CreateEncoding(BinaryTypeEncodingType type); + static v8::Local HandleOf(v8::Local context, + v8::Local value); + static v8::Local CallFunctionInternal(MethodCall& methodCall); + static bool IsNumbericType(BinaryTypeEncodingType type); + static v8::Local GetInteropType(v8::Local context, + BinaryTypeEncodingType type); + static std::vector GetAdditionalProtocols( + const TypeEncoding* typeEncoding); + static SEL GetSwizzledMethodSelector(SEL selector); + + template + static inline void SetValue(void* dest, T value) { + if (std::is_same::value) { + memcpy(dest, &value, sizeof(SEL*)); + } else { + *static_cast(dest) = value; } + } - typedef struct JSBlock { - typedef struct { - uintptr_t reserved; - uintptr_t size; - void (*copy)(struct JSBlock*, const struct JSBlock*); - void (*dispose)(struct JSBlock*); - } JSBlockDescriptor; - - enum { - BLOCK_NEEDS_FREE = (1 << 24), // runtime - BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler - }; - - void* isa; - volatile int32_t flags; // contains ref count - int32_t reserved; - const void* invoke; - JSBlockDescriptor* descriptor; - void* userData; - ffi_closure* ffiClosure; - - static JSBlockDescriptor kJSBlockDescriptor; - } JSBlock; - + template + static void SetNumericValue(void* dest, double value) { + if (value < std::numeric_limits::lowest()) { + Interop::SetValue(dest, std::numeric_limits::lowest()); + } else if (value > std::numeric_limits::max()) { + Interop::SetValue(dest, std::numeric_limits::max()); + } else { + Interop::SetValue(dest, (T)value); + } + } }; -} +} // namespace tns #endif /* Interop_h */ diff --git a/NativeScript/runtime/Interop.mm b/NativeScript/runtime/Interop.mm index b405accf..de85941f 100644 --- a/NativeScript/runtime/Interop.mm +++ b/NativeScript/runtime/Interop.mm @@ -1,24 +1,25 @@ +#include "Interop.h" #include #include -#include "Runtime.h" -#include "Interop.h" -#include "ObjectManager.h" -#include "Helpers.h" +#include "AOTDirectCalls.h" #include "ArgConverter.h" -#include "NativeScriptException.h" -#include "DictionaryAdapter.h" -#include "SymbolLoader.h" #include "ArrayAdapter.h" -#include "NSDataAdapter.h" -#include "Constants.h" #include "Caches.h" -#include "Reference.h" -#include "Pointer.h" +#include "Constants.h" +#include "DictionaryAdapter.h" #include "ExtVector.h" +#include "Helpers.h" +#include "NSDataAdapter.h" +#include "NativeScriptException.h" +#include "ObjectManager.h" +#include "OneByteStringResource.h" +#include "Pointer.h" +#include "Reference.h" +#include "Runtime.h" #include "RuntimeConfig.h" #include "SymbolIterator.h" +#include "SymbolLoader.h" #include "UnmanagedType.h" -#include "OneByteStringResource.h" #include "robin_hood.h" using namespace v8; @@ -26,1668 +27,1772 @@ namespace tns { static constexpr uint64_t kUint64AllBitsSet = static_cast(int64_t{-1}); -static constexpr int64_t kMinSafeInteger = static_cast(kUint64AllBitsSet << 53) + 1; // -9007199254740991 (-(2^53-1)) -static constexpr int64_t kMaxSafeInteger = -kMinSafeInteger; // 9007199254740991 (2^53-1) +static constexpr int64_t kMinSafeInteger = + static_cast(kUint64AllBitsSet << 53) + 1; // -9007199254740991 (-(2^53-1)) +static constexpr int64_t kMaxSafeInteger = -kMinSafeInteger; // 9007199254740991 (2^53-1) Interop::JSBlock::JSBlockDescriptor Interop::JSBlock::kJSBlockDescriptor = { .reserved = 0, .size = sizeof(JSBlock), - .copy = [](JSBlock* dst, const JSBlock* src) { - }, - .dispose = [](JSBlock* block) { - if (block->descriptor == &JSBlock::kJSBlockDescriptor) { + .copy = [](JSBlock* dst, const JSBlock* src) {}, + .dispose = + [](JSBlock* block) { + if (block->descriptor == &JSBlock::kJSBlockDescriptor) { MethodCallbackWrapper* wrapper = static_cast(block->userData); if (wrapper->isolateWrapper_.IsValid()) { - Isolate* isolate = wrapper->isolateWrapper_.Isolate(); - v8::Locker locker(isolate); - Isolate::Scope isolate_scope(isolate); - HandleScope handle_scope(isolate); - Local callback = wrapper->callback_->Get(isolate); - if (!callback.IsEmpty() && callback->IsObject()) { - BlockWrapper* blockWrapper = static_cast(tns::GetValue(isolate, callback)); - tns::DeleteValue(isolate, callback); - wrapper->callback_->Reset(); - delete blockWrapper; - } + Isolate* isolate = wrapper->isolateWrapper_.Isolate(); + v8::Locker locker(isolate); + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + Local callback = wrapper->callback_->Get(isolate); + if (!callback.IsEmpty() && callback->IsObject()) { + BlockWrapper* blockWrapper = + static_cast(tns::GetValue(isolate, callback)); + tns::DeleteValue(isolate, callback); + wrapper->callback_->Reset(); + delete blockWrapper; + } } delete wrapper; ffi_closure_free(block->ffiClosure); block->~JSBlock(); - } - } -}; - -std::pair Interop::CreateMethodInternal(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData) { - void* functionPointer; - ffi_closure* closure = static_cast(ffi_closure_alloc(sizeof(ffi_closure), &functionPointer)); - ParametrizedCall* call = ParametrizedCall::Get(typeEncoding, initialParamIndex, initialParamIndex + argsCount); - ffi_status status = ffi_prep_closure_loc(closure, call->Cif, callback, userData, functionPointer); - tns::Assert(status == FFI_OK); - - return std::make_pair((IMP)functionPointer, closure); - + } + }}; + +std::pair Interop::CreateMethodInternal(const uint8_t initialParamIndex, + const uint8_t argsCount, + const TypeEncoding* typeEncoding, + FFIMethodCallback callback, + void* userData) { + void* functionPointer; + ffi_closure* closure = + static_cast(ffi_closure_alloc(sizeof(ffi_closure), &functionPointer)); + ParametrizedCall* call = + ParametrizedCall::Get(typeEncoding, initialParamIndex, initialParamIndex + argsCount); + ffi_status status = ffi_prep_closure_loc(closure, call->Cif, callback, userData, functionPointer); + tns::Assert(status == FFI_OK); + + return std::make_pair((IMP)functionPointer, closure); } -IMP Interop::CreateMethod(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData) { - std::pair result = Interop::CreateMethodInternal(initialParamIndex, argsCount, typeEncoding, callback, userData); - return result.first; +IMP Interop::CreateMethod(const uint8_t initialParamIndex, const uint8_t argsCount, + const TypeEncoding* typeEncoding, FFIMethodCallback callback, + void* userData) { + std::pair result = + Interop::CreateMethodInternal(initialParamIndex, argsCount, typeEncoding, callback, userData); + return result.first; } -CFTypeRef Interop::CreateBlock(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData) { - JSBlock* blockPointer = reinterpret_cast(malloc(sizeof(JSBlock))); - - std::pair result = Interop::CreateMethodInternal(initialParamIndex, argsCount, typeEncoding, callback, userData); - - *blockPointer = { - .isa = nullptr, - // this block is created with a refcount of 1 - // this means we "own" it and need to free it - .flags = JSBlock::BLOCK_HAS_COPY_DISPOSE | JSBlock::BLOCK_NEEDS_FREE | (1 /* ref count */ << 1), - .reserved = 0, - .invoke = (void*)result.first, - .descriptor = &JSBlock::kJSBlockDescriptor, - .userData = userData, - .ffiClosure = result.second, - }; - - object_setClass((__bridge id)blockPointer, objc_getClass("__NSMallocBlock__")); - - // transfer ownership back to ARC (see refcount comment above) - return CFBridgingRelease(blockPointer); +CFTypeRef Interop::CreateBlock(const uint8_t initialParamIndex, const uint8_t argsCount, + const TypeEncoding* typeEncoding, FFIMethodCallback callback, + void* userData) { + JSBlock* blockPointer = reinterpret_cast(malloc(sizeof(JSBlock))); + + AOTBlockInvokeFunc aotInvoke = GetAOTBlockInvoke(typeEncoding, argsCount); + ffi_closure* closure = nullptr; + const void* invokePtr = nullptr; + + if (aotInvoke) { + invokePtr = aotInvoke; + } else { + std::pair result = Interop::CreateMethodInternal( + initialParamIndex, argsCount, typeEncoding, callback, userData); + invokePtr = (const void*)result.first; + closure = result.second; + } + + *blockPointer = { + .isa = nullptr, + // this block is created with a refcount of 1 + // this means we "own" it and need to free it + .flags = + JSBlock::BLOCK_HAS_COPY_DISPOSE | JSBlock::BLOCK_NEEDS_FREE | (1 /* ref count */ << 1), + .reserved = 0, + .invoke = (void*)invokePtr, + .descriptor = &JSBlock::kJSBlockDescriptor, + .userData = userData, + .ffiClosure = closure, + }; + + object_setClass((__bridge id)blockPointer, objc_getClass("__NSMallocBlock__")); + + // transfer ownership back to ARC (see refcount comment above) + return CFBridgingRelease(blockPointer); } Local Interop::CallFunction(CMethodCall& methodCall) { - return Interop::CallFunctionInternal(methodCall); + return Interop::CallFunctionInternal(methodCall); } Local Interop::CallFunction(ObjCMethodCall& methodCall) { - return Interop::CallFunctionInternal(methodCall); + return Interop::CallFunctionInternal(methodCall); } -id Interop::CallInitializer(Local context, const MethodMeta* methodMeta, id target, Class clazz, V8Args& args) { - const TypeEncoding* typeEncoding = methodMeta->encodings()->first(); - SEL selector = methodMeta->selector(); - void* functionPointer = (void*)objc_msgSend; +id Interop::CallInitializer(Local context, const MethodMeta* methodMeta, id target, + Class clazz, V8Args& args) { + const TypeEncoding* typeEncoding = methodMeta->encodings()->first(); + SEL selector = methodMeta->selector(); + void* functionPointer = (void*)objc_msgSend; - int initialParameterIndex = 2; - int argsCount = initialParameterIndex + (int)args.Length(); + int initialParameterIndex = 2; + int argsCount = initialParameterIndex + (int)args.Length(); - ParametrizedCall* parametrizedCall = ParametrizedCall::Get(typeEncoding, initialParameterIndex, argsCount); - FFICall call(parametrizedCall); + ParametrizedCall* parametrizedCall = + ParametrizedCall::Get(typeEncoding, initialParameterIndex, argsCount); + FFICall call(parametrizedCall); - Interop::SetValue(call.ArgumentBuffer(0), target); - Interop::SetValue(call.ArgumentBuffer(1), selector); - Interop::SetFFIParams(context, typeEncoding, &call, argsCount, initialParameterIndex, args); + Interop::SetValue(call.ArgumentBuffer(0), target); + Interop::SetValue(call.ArgumentBuffer(1), selector); + Interop::SetFFIParams(context, typeEncoding, &call, argsCount, initialParameterIndex, args); - ffi_call(parametrizedCall->Cif, FFI_FN(functionPointer), call.ResultBuffer(), call.ArgsArray()); + ffi_call(parametrizedCall->Cif, FFI_FN(functionPointer), call.ResultBuffer(), call.ArgsArray()); - id result = call.GetResult(); + id result = call.GetResult(); - return result; + return result; } -void Interop::SetFFIParams(Local context, const TypeEncoding* typeEncoding, FFICall* call, const int argsCount, const int initialParameterIndex, V8Args& args) { - const TypeEncoding* enc = typeEncoding; - for (int i = initialParameterIndex; i < argsCount; i++) { - enc = enc->next(); - Local arg = args[i - initialParameterIndex]; - void* argBuffer = call->ArgumentBuffer(i); - Interop::WriteValue(context, enc, argBuffer, arg); - } +void Interop::SetFFIParams(Local context, const TypeEncoding* typeEncoding, FFICall* call, + const int argsCount, const int initialParameterIndex, V8Args& args) { + const TypeEncoding* enc = typeEncoding; + for (int i = initialParameterIndex; i < argsCount; i++) { + enc = enc->next(); + Local arg = args[i - initialParameterIndex]; + void* argBuffer = call->ArgumentBuffer(i); + Interop::WriteValue(context, enc, argBuffer, arg); + } } -bool Interop::isRefTypeEqual(const TypeEncoding* typeEncoding, const char* clazz){ - std::string n(&typeEncoding->details.interfaceDeclarationReference.name.value()); - return n.compare(clazz) == 0; +bool Interop::isRefTypeEqual(const TypeEncoding* typeEncoding, const char* clazz) { + std::string n(&typeEncoding->details.interfaceDeclarationReference.name.value()); + return n.compare(clazz) == 0; } -// this is experimental. Maybe we can have something like this to wrap all Local to avoid extra v8 calls +// this is experimental. Maybe we can have something like this to wrap all Local to avoid +// extra v8 calls class ValueCache { - public: - enum IsOfType { - UNDEFINED, - TYPES_YES, - TYPE_NO - }; - - inline bool isObject() { - if(this->_isObject == UNDEFINED) { - this->_isObject = this->_arg->IsObject() ? TYPES_YES : TYPE_NO; - } - return this->_isObject == TYPES_YES; + public: + enum IsOfType { UNDEFINED, TYPES_YES, TYPE_NO }; + + inline bool isObject() { + if (this->_isObject == UNDEFINED) { + this->_isObject = this->_arg->IsObject() ? TYPES_YES : TYPE_NO; } - - inline bool isString() { - if(this->_isString == UNDEFINED) { - this->_isString = tns::IsString(this->_arg) ? TYPES_YES : TYPE_NO; - } - return this->_isString == TYPES_YES; + return this->_isObject == TYPES_YES; + } + + inline bool isString() { + if (this->_isString == UNDEFINED) { + this->_isString = tns::IsString(this->_arg) ? TYPES_YES : TYPE_NO; } - - inline bool isBool() { - if(this->_isBool == UNDEFINED) { - this->_isBool = tns::IsBool(this->_arg) ? TYPES_YES : TYPE_NO; - } - return this->_isBool == TYPES_YES; - } - - ValueCache(Local& arg) : _arg(arg) { - - }; - - private: - Local& _arg; - IsOfType _isString = UNDEFINED; - IsOfType _isBool = UNDEFINED; - IsOfType _isObject = UNDEFINED; + return this->_isString == TYPES_YES; + } + + inline bool isBool() { + if (this->_isBool == UNDEFINED) { + this->_isBool = tns::IsBool(this->_arg) ? TYPES_YES : TYPE_NO; + } + return this->_isBool == TYPES_YES; + } + + ValueCache(Local& arg) + : _arg(arg) { + + }; + + private: + Local& _arg; + IsOfType _isString = UNDEFINED; + IsOfType _isBool = UNDEFINED; + IsOfType _isObject = UNDEFINED; }; -void Interop::WriteTypeValue(Local context, BaseDataWrapper* typeWrapper, void* dest, Local arg) { - Isolate* isolate = context->GetIsolate(); - ValueCache argHelper(arg); - bool isEmptyOrUndefined = arg.IsEmpty() || arg->IsNullOrUndefined(); - bool success = false; - - if (typeWrapper->Type() == WrapperType::StructType) { - if (isEmptyOrUndefined) { - StructTypeWrapper* structTypeWrapper = static_cast(typeWrapper); - StructInfo structInfo = structTypeWrapper->StructInfo(); - - memset(dest, 0, structInfo.FFIType()->size); - success = true; - } else if (argHelper.isObject()) { - BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); - if (wrapper != nullptr) { - if (wrapper->Type() == WrapperType::Struct) { - StructWrapper* structWrapper = static_cast(wrapper); - void* buffer = structWrapper->Data(); - size_t size = structWrapper->StructInfo().FFIType()->size; - memcpy(dest, buffer, size); - success = true; - } - } else { - // Create the structure using the struct initializer syntax - StructTypeWrapper* structTypeWrapper = static_cast(typeWrapper); - StructInfo structInfo = structTypeWrapper->StructInfo(); - Interop::InitializeStruct(context, dest, structInfo.Fields(), arg.As()); - success = true; - } - } +void Interop::WriteTypeValue(Local context, BaseDataWrapper* typeWrapper, void* dest, + Local arg) { + Isolate* isolate = context->GetIsolate(); + ValueCache argHelper(arg); + bool isEmptyOrUndefined = arg.IsEmpty() || arg->IsNullOrUndefined(); + bool success = false; + + if (typeWrapper->Type() == WrapperType::StructType) { + if (isEmptyOrUndefined) { + StructTypeWrapper* structTypeWrapper = static_cast(typeWrapper); + StructInfo structInfo = structTypeWrapper->StructInfo(); + + memset(dest, 0, structInfo.FFIType()->size); + success = true; + } else if (argHelper.isObject()) { + BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); + if (wrapper != nullptr) { + if (wrapper->Type() == WrapperType::Struct) { + StructWrapper* structWrapper = static_cast(wrapper); + void* buffer = structWrapper->Data(); + size_t size = structWrapper->StructInfo().FFIType()->size; + memcpy(dest, buffer, size); + success = true; + } + } else { + // Create the structure using the struct initializer syntax + StructTypeWrapper* structTypeWrapper = static_cast(typeWrapper); + StructInfo structInfo = structTypeWrapper->StructInfo(); + Interop::InitializeStruct(context, dest, structInfo.Fields(), arg.As()); + success = true; + } + } + } + + if (!success) { + tns::Assert(false, isolate); + } +} + +void Interop::WriteValue(Local context, const TypeEncoding* typeEncoding, void* dest, + Local arg) { + Isolate* isolate = context->GetIsolate(); + ExecuteWriteValueDebugValidationsIfInDebug(context, typeEncoding, dest, arg); + ValueCache argHelper(arg); + if (arg.IsEmpty() || arg->IsNullOrUndefined()) { + ffi_type* ffiType = FFICall::GetArgumentType(typeEncoding, true); + size_t size = ffiType->size; + FFICall::DisposeFFIType(ffiType, typeEncoding); + memset(dest, 0, size); + } else if (argHelper.isBool()) { + if (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference && + isRefTypeEqual(typeEncoding, "NSNumber")) { + bool value = tns::ToBool(arg); + NSNumber* num = [NSNumber numberWithBool:value]; + Interop::SetValue(dest, num); + } else if (typeEncoding->type == BinaryTypeEncodingType::IdEncoding) { + bool value = tns::ToBool(arg); + NSObject* o = @(value); + Interop::SetValue(dest, o); + } else { + bool value = tns::ToBool(arg); + Interop::SetValue(dest, value); } - - if (!success) { + } else if (argHelper.isString() && + typeEncoding->type == BinaryTypeEncodingType::SelectorEncoding) { + std::string str = tns::ToString(isolate, arg); + NSString* selStr = [NSString stringWithUTF8String:str.c_str()]; + SEL selector = NSSelectorFromString(selStr); + Interop::SetValue(dest, selector); + } else if (typeEncoding->type == BinaryTypeEncodingType::CStringEncoding) { + if (arg->IsString()) { + const char* value = nullptr; + Local strArg = arg.As(); + if (strArg->IsExternalOneByte()) { + const v8::String::ExternalOneByteStringResource* resource = + strArg->GetExternalOneByteStringResource(); + value = resource->data(); + } else { + v8::String::Utf8Value utf8Value(isolate, arg); + value = strdup(*utf8Value); + auto length = strArg->Length() + 1; + OneByteStringResource* resource = new OneByteStringResource(value, length); + bool success = v8::String::NewExternalOneByte(isolate, resource).ToLocal(&arg); + tns::Assert(success, isolate); + } + Interop::SetValue(dest, value); + } else { + BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); + if (wrapper == nullptr) { + bool isArrayBuffer = false; + void* data = tns::TryGetBufferFromArrayBuffer(arg, isArrayBuffer); + if (isArrayBuffer) { + Interop::SetValue(dest, data); + return; + } + } + + tns::Assert(wrapper != nullptr, isolate); + if (wrapper->Type() == WrapperType::Pointer) { + PointerWrapper* pw = static_cast(wrapper); + void* data = pw->Data(); + Interop::SetValue(dest, data); + } else if (wrapper->Type() == WrapperType::Reference) { + ReferenceWrapper* refWrapper = static_cast(wrapper); + tns::Assert(refWrapper->Value() != nullptr, isolate); + Local value = refWrapper->Value()->Get(isolate); + wrapper = tns::GetValue(isolate, value); + tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::Pointer, isolate); + PointerWrapper* pw = static_cast(wrapper); + void* data = pw->Data(); + Interop::SetValue(dest, data); + } else { + // Unsupported wrapprt type for CString tns::Assert(false, isolate); + } } -} + } else if (argHelper.isString() && + typeEncoding->type == BinaryTypeEncodingType::UnicharEncoding) { + v8::String::Utf8Value utf8Value(isolate, arg); + std::vector vector = tns::ToVector(*utf8Value); + if (vector.size() > 1) { + throw NativeScriptException("Only one character string can be converted to unichar."); + } + unichar c = (vector.size() == 0) ? 0 : vector[0]; + Interop::SetValue(dest, c); + } else if (argHelper.isString() && + (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference || + typeEncoding->type == BinaryTypeEncodingType::IdEncoding)) { + NSString* result = tns::ToNSString(isolate, arg); + Interop::SetValue(dest, result); + } else if (Interop::IsNumbericType(typeEncoding->type) || tns::IsNumber(arg)) { + double value = tns::ToNumber(isolate, arg); -void Interop::WriteValue(Local context, const TypeEncoding* typeEncoding, void* dest, Local arg) { - Isolate* isolate = context->GetIsolate(); - ExecuteWriteValueDebugValidationsIfInDebug(context, typeEncoding, dest, arg); - ValueCache argHelper(arg); - if (arg.IsEmpty() || arg->IsNullOrUndefined()) { - ffi_type* ffiType = FFICall::GetArgumentType(typeEncoding, true); - size_t size = ffiType->size; - FFICall::DisposeFFIType(ffiType, typeEncoding); - memset(dest, 0, size); - } else if (argHelper.isBool()) { - if(typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference && isRefTypeEqual(typeEncoding, "NSNumber")) { - bool value = tns::ToBool(arg); - NSNumber *num = [NSNumber numberWithBool: value]; - Interop::SetValue(dest, num); - } else if(typeEncoding->type == BinaryTypeEncodingType::IdEncoding) { - bool value = tns::ToBool(arg); - NSObject* o = @(value); - Interop::SetValue(dest, o); - } else { - bool value = tns::ToBool(arg); - Interop::SetValue(dest, value); - } - } else if (argHelper.isString() && typeEncoding->type == BinaryTypeEncodingType::SelectorEncoding) { - std::string str = tns::ToString(isolate, arg); - NSString* selStr = [NSString stringWithUTF8String:str.c_str()]; - SEL selector = NSSelectorFromString(selStr); - Interop::SetValue(dest, selector); - } else if (typeEncoding->type == BinaryTypeEncodingType::CStringEncoding) { - if (arg->IsString()) { - const char* value = nullptr; - Local strArg = arg.As(); - if (strArg->IsExternalOneByte()) { - const v8::String::ExternalOneByteStringResource* resource = strArg->GetExternalOneByteStringResource(); - value = resource->data(); - } else { - v8::String::Utf8Value utf8Value(isolate, arg); - value = strdup(*utf8Value); - auto length = strArg->Length() + 1; - OneByteStringResource* resource = new OneByteStringResource(value, length); - bool success = v8::String::NewExternalOneByte(isolate, resource).ToLocal(&arg); - tns::Assert(success, isolate); - } - Interop::SetValue(dest, value); - } else { - BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); - if (wrapper == nullptr) { - bool isArrayBuffer = false; - void* data = tns::TryGetBufferFromArrayBuffer(arg, isArrayBuffer); - if (isArrayBuffer) { - Interop::SetValue(dest, data); - return; - } - } + if (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference || + typeEncoding->type == BinaryTypeEncodingType::IdEncoding) { + // NSNumber + NSNumber* num = [NSNumber numberWithDouble:value]; + Interop::SetValue(dest, num); + } else if (typeEncoding->type == BinaryTypeEncodingType::UShortEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::ShortEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::UIntEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::IntEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::ULongEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::LongEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::ULongLongEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::LongLongEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::FloatEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::DoubleEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::UCharEncoding) { + Interop::SetNumericValue(dest, value); + } else if (typeEncoding->type == BinaryTypeEncodingType::CharEncoding) { + Interop::SetNumericValue(dest, value); + } else { + tns::Assert(false, isolate); + } + } else if (typeEncoding->type == BinaryTypeEncodingType::ExtVectorEncoding) { + BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); + tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::ExtVector, isolate); + ExtVectorWrapper* extVectorWrapper = static_cast(wrapper); + void* data = extVectorWrapper->Data(); + size_t size = extVectorWrapper->FFIType()->size; + memcpy(dest, data, size); + } else if (typeEncoding->type == BinaryTypeEncodingType::PointerEncoding) { + const TypeEncoding* innerType = typeEncoding->details.pointer.getInnerType(); + BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); + if (innerType->type == BinaryTypeEncodingType::VoidEncoding) { + bool isArrayBuffer = false; + void* buffer = tns::TryGetBufferFromArrayBuffer(arg, isArrayBuffer); + if (isArrayBuffer) { + Interop::SetValue(dest, buffer); + return; + } - tns::Assert(wrapper != nullptr, isolate); - if (wrapper->Type() == WrapperType::Pointer) { - PointerWrapper* pw = static_cast(wrapper); - void* data = pw->Data(); - Interop::SetValue(dest, data); - } else if (wrapper->Type() == WrapperType::Reference) { - ReferenceWrapper* refWrapper = static_cast(wrapper); - tns::Assert(refWrapper->Value() != nullptr, isolate); - Local value = refWrapper->Value()->Get(isolate); - wrapper = tns::GetValue(isolate, value); - tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::Pointer, isolate); - PointerWrapper* pw = static_cast(wrapper); - void* data = pw->Data(); - Interop::SetValue(dest, data); - } else { - // Unsupported wrapprt type for CString - tns::Assert(false, isolate); - } - } - } else if (argHelper.isString() && typeEncoding->type == BinaryTypeEncodingType::UnicharEncoding) { - v8::String::Utf8Value utf8Value(isolate, arg); - std::vector vector = tns::ToVector(*utf8Value); - if (vector.size() > 1) { - throw NativeScriptException("Only one character string can be converted to unichar."); - } - unichar c = (vector.size() == 0) ? 0 : vector[0]; - Interop::SetValue(dest, c); - } else if (argHelper.isString() && (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference || typeEncoding->type == BinaryTypeEncodingType::IdEncoding)) { - NSString* result = tns::ToNSString(isolate, arg); - Interop::SetValue(dest, result); - } else if (Interop::IsNumbericType(typeEncoding->type) || tns::IsNumber(arg)) { - double value = tns::ToNumber(isolate, arg); - - if (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference || typeEncoding->type == BinaryTypeEncodingType::IdEncoding) { - // NSNumber - NSNumber* num = [NSNumber numberWithDouble:value]; - Interop::SetValue(dest, num); - } else if (typeEncoding->type == BinaryTypeEncodingType::UShortEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::ShortEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::UIntEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::IntEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::ULongEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::LongEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::ULongLongEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::LongLongEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::FloatEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::DoubleEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::UCharEncoding) { - Interop::SetNumericValue(dest, value); - } else if (typeEncoding->type == BinaryTypeEncodingType::CharEncoding) { - Interop::SetNumericValue(dest, value); - } else { - tns::Assert(false, isolate); - } - } else if (typeEncoding->type == BinaryTypeEncodingType::ExtVectorEncoding) { - BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); - tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::ExtVector, isolate); - ExtVectorWrapper* extVectorWrapper = static_cast(wrapper); - void* data = extVectorWrapper->Data(); - size_t size = extVectorWrapper->FFIType()->size; - memcpy(dest, data, size); - } else if (typeEncoding->type == BinaryTypeEncodingType::PointerEncoding) { - const TypeEncoding* innerType = typeEncoding->details.pointer.getInnerType(); - BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); - if (innerType->type == BinaryTypeEncodingType::VoidEncoding) { - bool isArrayBuffer = false; - void* buffer = tns::TryGetBufferFromArrayBuffer(arg, isArrayBuffer); - if (isArrayBuffer) { - Interop::SetValue(dest, buffer); - return; - } + tns::Assert(wrapper != nullptr, isolate); - tns::Assert(wrapper != nullptr, isolate); - - if (wrapper->Type() == WrapperType::Pointer) { - PointerWrapper* pointerWrapper = static_cast(wrapper); - void* data = pointerWrapper->Data(); - Interop::SetValue(dest, data); - } else if (wrapper->Type() == WrapperType::Reference) { - void* data = Reference::GetWrappedPointer(context, arg, typeEncoding); - Interop::SetValue(dest, data); - } else { - // TODO: - tns::Assert(false, isolate); - } - } else { - void* data = nullptr; - - if (wrapper == nullptr && innerType->type == BinaryTypeEncodingType::StructDeclarationReference) { - const Meta* meta = ArgConverter::GetMeta(innerType->details.declarationReference.name.valuePtr()); - tns::Assert(meta != nullptr && meta->type() == MetaType::Struct, isolate); - const StructMeta* structMeta = static_cast(meta); - StructInfo structInfo = FFICall::GetStructInfo(structMeta); - // TODO: How to free this? - // this is used when you have js obj and wants to pass the data as a struct ponter (MyStruct*) - // we create a new MyStruct with a snapshot of the jsObject and pass that in - // but when should we delete it? - // currently it's up to the function called to delete it - // we could delete after the function call, but if the fuction stores that then it's a memory leak - // we could also just store it as a wrapper in the object, binding it to the object lifecycle - // but that also means refactoring a lot of "if(wrapper == nullptr)" because essentially the wrapper - // should be treated as a nullptr, except when deating with StructDeclarationReference - data = malloc(structInfo.FFIType()->size); - Interop::InitializeStruct(context, data, structInfo.Fields(), arg); - } else { - if (wrapper == nullptr) { - bool isArrayBuffer = false; - void* data = tns::TryGetBufferFromArrayBuffer(arg, isArrayBuffer); - if (isArrayBuffer) { - Interop::SetValue(dest, data); - return; - } - } - - tns::Assert(wrapper != nullptr, isolate); - - if (wrapper->Type() == WrapperType::Pointer) { - PointerWrapper* pointerWrapper = static_cast(wrapper); - data = pointerWrapper->Data(); - } else if (wrapper->Type() == WrapperType::Reference) { - ReferenceWrapper* referenceWrapper = static_cast(wrapper); - Local value = referenceWrapper->Value() != nullptr ? referenceWrapper->Value()->Get(isolate) : Local(); - ffi_type* ffiType = FFICall::GetArgumentType(innerType); - data = calloc(1, ffiType->size); - FFICall::DisposeFFIType(ffiType, innerType); - - referenceWrapper->SetData(data, true); - referenceWrapper->SetEncoding(innerType); - - // Initialize the ref/out parameter value before passing it to the function call - if (!value.IsEmpty()) { - Interop::WriteValue(context, innerType, data, value); - } - } else if (wrapper->Type() == WrapperType::Struct) { - StructWrapper* structWrapper = static_cast(wrapper); - data = structWrapper->Data(); - } else { - tns::Assert(false, isolate); - } - } + if (wrapper->Type() == WrapperType::Pointer) { + PointerWrapper* pointerWrapper = static_cast(wrapper); + void* data = pointerWrapper->Data(); + Interop::SetValue(dest, data); + } else if (wrapper->Type() == WrapperType::Reference) { + void* data = Reference::GetWrappedPointer(context, arg, typeEncoding); + Interop::SetValue(dest, data); + } else { + // TODO: + tns::Assert(false, isolate); + } + } else { + void* data = nullptr; + if (wrapper == nullptr && + innerType->type == BinaryTypeEncodingType::StructDeclarationReference) { + const Meta* meta = + ArgConverter::GetMeta(innerType->details.declarationReference.name.valuePtr()); + tns::Assert(meta != nullptr && meta->type() == MetaType::Struct, isolate); + const StructMeta* structMeta = static_cast(meta); + StructInfo structInfo = FFICall::GetStructInfo(structMeta); + // TODO: How to free this? + // this is used when you have js obj and wants to pass the data as a struct ponter + // (MyStruct*) we create a new MyStruct with a snapshot of the jsObject and pass that in but + // when should we delete it? currently it's up to the function called to delete it we could + // delete after the function call, but if the fuction stores that then it's a memory leak we + // could also just store it as a wrapper in the object, binding it to the object lifecycle + // but that also means refactoring a lot of "if(wrapper == nullptr)" because essentially the + // wrapper should be treated as a nullptr, except when deating with + // StructDeclarationReference + data = malloc(structInfo.FFIType()->size); + Interop::InitializeStruct(context, data, structInfo.Fields(), arg); + } else { + if (wrapper == nullptr) { + bool isArrayBuffer = false; + void* data = tns::TryGetBufferFromArrayBuffer(arg, isArrayBuffer); + if (isArrayBuffer) { Interop::SetValue(dest, data); + return; + } } - } else if (argHelper.isObject() && typeEncoding->type == BinaryTypeEncodingType::FunctionPointerEncoding) { - BaseDataWrapper* wrapper = tns::GetValue(isolate, arg.As()); + tns::Assert(wrapper != nullptr, isolate); - if (wrapper->Type() == WrapperType::Pointer) { - PointerWrapper* pointerWrapper = static_cast(wrapper); - void* data = pointerWrapper->Data(); - Interop::SetValue(dest, data); - } else if (wrapper->Type() == WrapperType::AnonymousFunction) { - AnonymousFunctionWrapper* functionWrapper = static_cast(wrapper); - void* data = functionWrapper->Data(); - Interop::SetValue(dest, data); - } else if (wrapper->Type() == WrapperType::FunctionReference) { - tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::FunctionReference, isolate); - FunctionReferenceWrapper* funcWrapper = static_cast(wrapper); - const TypeEncoding* functionTypeEncoding = typeEncoding->details.functionPointer.signature.first(); - int argsCount = typeEncoding->details.functionPointer.signature.count - 1; - Local callbackValue = funcWrapper->Function()->Get(isolate); - tns::Assert(callbackValue->IsFunction(), isolate); - Local callback = callbackValue.As(); - std::shared_ptr> poCallback = std::make_shared>(isolate, callback); - MethodCallbackWrapper* userData = new MethodCallbackWrapper(isolate, poCallback, 0, argsCount, functionTypeEncoding); + if (wrapper->Type() == WrapperType::Pointer) { + PointerWrapper* pointerWrapper = static_cast(wrapper); + data = pointerWrapper->Data(); + } else if (wrapper->Type() == WrapperType::Reference) { + ReferenceWrapper* referenceWrapper = static_cast(wrapper); + Local value = referenceWrapper->Value() != nullptr + ? referenceWrapper->Value()->Get(isolate) + : Local(); + ffi_type* ffiType = FFICall::GetArgumentType(innerType); + data = calloc(1, ffiType->size); + FFICall::DisposeFFIType(ffiType, innerType); + + referenceWrapper->SetData(data, true); + referenceWrapper->SetEncoding(innerType); + + // Initialize the ref/out parameter value before passing it to the function call + if (!value.IsEmpty()) { + Interop::WriteValue(context, innerType, data, value); + } + } else if (wrapper->Type() == WrapperType::Struct) { + StructWrapper* structWrapper = static_cast(wrapper); + data = structWrapper->Data(); + } else { + tns::Assert(false, isolate); + } + } - void* functionPointer = (void*)Interop::CreateMethod(0, argsCount, functionTypeEncoding, ArgConverter::MethodCallback, userData); + Interop::SetValue(dest, data); + } + } else if (argHelper.isObject() && + typeEncoding->type == BinaryTypeEncodingType::FunctionPointerEncoding) { + BaseDataWrapper* wrapper = tns::GetValue(isolate, arg.As()); + tns::Assert(wrapper != nullptr, isolate); + if (wrapper->Type() == WrapperType::Pointer) { + PointerWrapper* pointerWrapper = static_cast(wrapper); + void* data = pointerWrapper->Data(); + Interop::SetValue(dest, data); + } else if (wrapper->Type() == WrapperType::AnonymousFunction) { + AnonymousFunctionWrapper* functionWrapper = static_cast(wrapper); + void* data = functionWrapper->Data(); + Interop::SetValue(dest, data); + } else if (wrapper->Type() == WrapperType::FunctionReference) { + tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::FunctionReference, isolate); + FunctionReferenceWrapper* funcWrapper = static_cast(wrapper); + const TypeEncoding* functionTypeEncoding = + typeEncoding->details.functionPointer.signature.first(); + int argsCount = typeEncoding->details.functionPointer.signature.count - 1; + + Local callbackValue = funcWrapper->Function()->Get(isolate); + tns::Assert(callbackValue->IsFunction(), isolate); + Local callback = callbackValue.As(); + std::shared_ptr> poCallback = + std::make_shared>(isolate, callback); + MethodCallbackWrapper* userData = + new MethodCallbackWrapper(isolate, poCallback, 0, argsCount, functionTypeEncoding); + + void* functionPointer = (void*)Interop::CreateMethod(0, argsCount, functionTypeEncoding, + ArgConverter::MethodCallback, userData); + + funcWrapper->SetData(functionPointer); + + Interop::SetValue(dest, functionPointer); + } else { + tns::Assert(false, isolate); + } + } else if (arg->IsFunction() && typeEncoding->type == BinaryTypeEncodingType::BlockEncoding) { + const TypeEncoding* blockTypeEncoding = typeEncoding->details.block.signature.first(); + int argsCount = typeEncoding->details.block.signature.count - 1; + + CFTypeRef blockPtr = nullptr; + BaseDataWrapper* baseWrapper = tns::GetValue(isolate, arg); + if (baseWrapper != nullptr && baseWrapper->Type() == WrapperType::Block) { + BlockWrapper* wrapper = static_cast(baseWrapper); + blockPtr = Block_copy(wrapper->Block()); + } else { + std::shared_ptr> poCallback = + std::make_shared>(isolate, arg); + MethodCallbackWrapper* userData = + new MethodCallbackWrapper(isolate, poCallback, 1, argsCount, blockTypeEncoding); + blockPtr = Interop::CreateBlock(1, argsCount, blockTypeEncoding, ArgConverter::MethodCallback, + userData); + + BlockWrapper* wrapper = new BlockWrapper((void*)blockPtr, blockTypeEncoding, false); + tns::SetValue(isolate, arg.As(), wrapper); + } - funcWrapper->SetData(functionPointer); + Interop::SetValue(dest, blockPtr); + } else if (argHelper.isObject() && + typeEncoding->type == BinaryTypeEncodingType::StructDeclarationReference) { + Local obj = arg.As(); + BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); + if (wrapper != nullptr) { + if (wrapper->Type() == WrapperType::Struct) { + StructWrapper* structWrapper = static_cast(wrapper); + void* buffer = structWrapper->Data(); + size_t size = structWrapper->StructInfo().FFIType()->size; + memcpy(dest, buffer, size); + } else if (wrapper->Type() == WrapperType::Reference) { + void* data = Reference::GetWrappedPointer(context, arg, typeEncoding); + tns::Assert(data != nullptr, isolate); + ffi_type* ffiType = FFICall::GetArgumentType(typeEncoding); + size_t size = ffiType->size; + FFICall::DisposeFFIType(ffiType, typeEncoding); + memcpy(dest, data, size); + } else { + tns::Assert(false, isolate); + } + } else { + // Create the structure using the struct initializer syntax + const char* structName = typeEncoding->details.declarationReference.name.valuePtr(); + const Meta* meta = ArgConverter::GetMeta(structName); + tns::Assert(meta != nullptr && meta->type() == MetaType::Struct, isolate); + const StructMeta* structMeta = static_cast(meta); + StructInfo structInfo = FFICall::GetStructInfo(structMeta); + Interop::InitializeStruct(context, dest, structInfo.Fields(), obj); + } + } else if (argHelper.isObject() && + typeEncoding->type == BinaryTypeEncodingType::AnonymousStructEncoding) { + Local obj = arg.As(); + BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); + if (wrapper != nullptr && wrapper->Type() == WrapperType::Struct) { + size_t fieldsCount = typeEncoding->details.anonymousRecord.fieldsCount; + const TypeEncoding* fieldEncoding = + typeEncoding->details.anonymousRecord.getFieldsEncodings(); + const String* fieldNames = typeEncoding->details.anonymousRecord.getFieldNames(); + StructInfo structInfo = FFICall::GetStructInfo(fieldsCount, fieldEncoding, fieldNames); + + StructWrapper* structWrapper = static_cast(wrapper); + void* data = structWrapper->Data(); + size_t size = structInfo.FFIType()->size; + memcpy(dest, data, size); + } else { + // Anonymous structs can only be initialized with plain javascript objects + tns::Assert(wrapper == nullptr, isolate); + size_t fieldsCount = typeEncoding->details.anonymousRecord.fieldsCount; + const TypeEncoding* fieldEncoding = + typeEncoding->details.anonymousRecord.getFieldsEncodings(); + const String* fieldNames = typeEncoding->details.anonymousRecord.getFieldNames(); + StructInfo structInfo = FFICall::GetStructInfo(fieldsCount, fieldEncoding, fieldNames); + Interop::InitializeStruct(context, dest, structInfo.Fields(), obj); + } + } else if (arg->IsFunction() && typeEncoding->type == BinaryTypeEncodingType::ProtocolEncoding) { + Local obj = arg.As(); + BaseDataWrapper* wrapper = tns::GetValue(isolate, obj); + tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::ObjCProtocol, isolate); + ObjCProtocolWrapper* protoWrapper = static_cast(wrapper); + Protocol* proto = protoWrapper->Proto(); + Interop::SetValue(dest, proto); + } else if (argHelper.isObject() && typeEncoding->type == BinaryTypeEncodingType::ClassEncoding) { + Local obj = arg.As(); + BaseDataWrapper* wrapper = tns::GetValue(isolate, obj); + tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::ObjCClass, isolate); + ObjCClassWrapper* classWrapper = static_cast(wrapper); + Class clazz = classWrapper->Klass(); + Interop::SetValue(dest, clazz); + } else if (arg->IsDate()) { + Local date = arg.As(); + double time = date->ValueOf(); + NSDate* nsDate = [NSDate dateWithTimeIntervalSince1970:(time / 1000)]; + Interop::SetValue(dest, nsDate); + } else if (typeEncoding->type == BinaryTypeEncodingType::IncompleteArrayEncoding) { + void* data = nullptr; + if (arg->IsArrayBuffer()) { + std::shared_ptr backingStore = arg.As()->GetBackingStore(); + data = backingStore->Data(); + } else if (arg->IsArrayBufferView()) { + std::shared_ptr backingStore = + arg.As()->Buffer()->GetBackingStore(); + data = backingStore->Data(); + } else { + data = Reference::GetWrappedPointer(context, arg, typeEncoding); + } + Interop::SetValue(dest, data); + } else if (typeEncoding->type == BinaryTypeEncodingType::ConstantArrayEncoding) { + if (arg->IsArray()) { + Local array = arg.As(); + Local context = isolate->GetCurrentContext(); + const TypeEncoding* innerType = typeEncoding->details.constantArray.getInnerType(); + ffi_type* ffiType = FFICall::GetArgumentType(innerType); + uint32_t length = array->Length(); + for (uint32_t i = 0; i < length; i++) { + Local element; + bool success = array->Get(context, i).ToLocal(&element); + tns::Assert(success, isolate); + void* ptr = (uint8_t*)dest + i * ffiType->size; + Interop::WriteValue(context, innerType, ptr, element); + } + FFICall::DisposeFFIType(ffiType, innerType); + } else { + void* data = Reference::GetWrappedPointer(context, arg, typeEncoding); + Interop::SetValue(dest, data); + } + } else if (argHelper.isObject()) { + Local obj = arg.As(); + BaseDataWrapper* wrapper = tns::GetValue(isolate, obj); - Interop::SetValue(dest, functionPointer); - } else { - tns::Assert(false, isolate); + if (wrapper != nullptr) { + if (wrapper->Type() == WrapperType::Enum) { + EnumDataWrapper* enumWrapper = static_cast(wrapper); + Local context = isolate->GetCurrentContext(); + std::string jsCode = enumWrapper->JSCode(); + Local