#include #include "SymbolLoader.h" #include "Interop.h" #include "ObjectManager.h" #include "Helpers.h" #include "Caches.h" #include "NativeScriptException.h" #include "FunctionReference.h" #include "Reference.h" #include "Pointer.h" #include "Constants.h" using namespace v8; namespace tns { void Interop::RegisterInteropTypes(Local context) { Isolate* isolate = context->GetIsolate(); Local global = context->Global(); Local interop = Object::New(isolate); Local types = Object::New(isolate); Reference::Register(context, interop); Pointer::Register(context, interop); FunctionReference::Register(context, interop); RegisterBufferFromDataFunction(context, interop); RegisterHandleOfFunction(context, interop); RegisterAllocFunction(context, interop); RegisterFreeFunction(context, interop); RegisterAdoptFunction(context, interop); RegisterSizeOfFunction(context, interop); RegisterInteropType(context, types, "noop", MakeGarbageCollected(isolate, ffi_type_pointer.size, BinaryTypeEncodingType::VoidEncoding)); RegisterInteropType(context, types, "void", MakeGarbageCollected(isolate, 0, BinaryTypeEncodingType::VoidEncoding)); RegisterInteropType(context, types, "bool", MakeGarbageCollected(isolate, sizeof(bool), BinaryTypeEncodingType::BoolEncoding)); RegisterInteropType(context, types, "uint8", MakeGarbageCollected(isolate, ffi_type_uint8.size, BinaryTypeEncodingType::UCharEncoding)); RegisterInteropType(context, types, "int8", MakeGarbageCollected(isolate, ffi_type_sint8.size, BinaryTypeEncodingType::CharEncoding)); RegisterInteropType(context, types, "uint16", MakeGarbageCollected(isolate, ffi_type_uint16.size, BinaryTypeEncodingType::UShortEncoding)); RegisterInteropType(context, types, "int16", MakeGarbageCollected(isolate, ffi_type_sint16.size, BinaryTypeEncodingType::ShortEncoding)); RegisterInteropType(context, types, "uint32", MakeGarbageCollected(isolate, ffi_type_uint32.size, BinaryTypeEncodingType::UIntEncoding)); RegisterInteropType(context, types, "int32", MakeGarbageCollected(isolate, ffi_type_sint32.size, BinaryTypeEncodingType::IntEncoding)); RegisterInteropType(context, types, "uint64", MakeGarbageCollected(isolate, ffi_type_uint64.size, BinaryTypeEncodingType::ULongEncoding)); RegisterInteropType(context, types, "int64", MakeGarbageCollected(isolate, ffi_type_sint64.size, BinaryTypeEncodingType::LongEncoding)); RegisterInteropType(context, types, "ulong", MakeGarbageCollected(isolate, ffi_type_ulong.size, BinaryTypeEncodingType::ULongLongEncoding)); RegisterInteropType(context, types, "slong", MakeGarbageCollected(isolate, ffi_type_slong.size, BinaryTypeEncodingType::LongLongEncoding)); RegisterInteropType(context, types, "float", MakeGarbageCollected(isolate, ffi_type_float.size, BinaryTypeEncodingType::FloatEncoding)); RegisterInteropType(context, types, "double", MakeGarbageCollected(isolate, ffi_type_double.size, BinaryTypeEncodingType::DoubleEncoding)); RegisterInteropType(context, types, "id", MakeGarbageCollected(isolate, sizeof(void*), BinaryTypeEncodingType::IdEncoding)); // RegisterInteropType(context, types, "UTF8CString", new PrimitiveDataWrapper(sizeof(void*), BinaryTypeEncodingType::VoidEncoding))); RegisterInteropType(context, types, "unichar", MakeGarbageCollected(isolate, ffi_type_ushort.size, BinaryTypeEncodingType::UnicharEncoding)); RegisterInteropType(context, types, "protocol", MakeGarbageCollected(isolate, sizeof(void*), BinaryTypeEncodingType::ProtocolEncoding)); RegisterInteropType(context, types, "class", MakeGarbageCollected(isolate, sizeof(void*), BinaryTypeEncodingType::ClassEncoding)); RegisterInteropType(context, types, "selector", MakeGarbageCollected(isolate, sizeof(void*), BinaryTypeEncodingType::SelectorEncoding)); bool success = interop->Set(context, tns::ToV8String(isolate, "types"), types).FromMaybe(false); tns::Assert(success, isolate); success = global->Set(context, tns::ToV8String(isolate, "interop"), interop).FromMaybe(false); tns::Assert(success, isolate); } Local Interop::GetInteropType(Local context, BinaryTypeEncodingType type) { Isolate* isolate = context->GetIsolate(); std::shared_ptr cache = Caches::Get(isolate); auto it = cache->PrimitiveInteropTypes.find(type); if (it != cache->PrimitiveInteropTypes.end()) { return it->second->Get(isolate); } return Local(); } void Interop::RegisterInteropType(Local context, Local types, std::string name, PrimitiveDataWrapper* wrapper, bool autoDelete) { Isolate* isolate = context->GetIsolate(); Local ctorFuncTemplate = FunctionTemplate::New(isolate, nullptr); ctorFuncTemplate->SetClassName(tns::ToV8String(isolate, name)); ctorFuncTemplate->InstanceTemplate()->SetInternalFieldCount(1); Local ctorFunc; if (!ctorFuncTemplate->GetFunction(context).ToLocal(&ctorFunc)) { tns::Assert(false, isolate); } Local value; if (!ctorFunc->CallAsConstructor(context, 0, nullptr).ToLocal(&value) || value.IsEmpty() || !value->IsObject()) { tns::Assert(false, isolate); } Local result = value.As(); tns::SetValue(isolate, result, wrapper); bool success = types->Set(context, tns::ToV8String(isolate, name), result).FromMaybe(false); BinaryTypeEncodingType type = wrapper->TypeEncoding()->type; std::shared_ptr cache = Caches::Get(isolate); auto it = cache->PrimitiveInteropTypes.find(type); if (it == cache->PrimitiveInteropTypes.end()) { auto persistentObj = std::make_unique>(isolate, result); if (autoDelete) { persistentObj->SetWrapperClassId(Constants::ClassTypes::DataWrapper); } cache->PrimitiveInteropTypes.emplace(type, std::move(persistentObj)); } else if (autoDelete) { // TODO: review this. We send the void encoding multiple times so this is just a dirty fallback // maybe we should have another method on the ObjectManager for cleaning up these cache->registerCacheBoundObject(wrapper); } tns::Assert(success, isolate); } void Interop::RegisterBufferFromDataFunction(Local context, Local interop) { Local func; bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); tns::Assert(info.Length() == 1 && info[0]->IsObject(), isolate); Local arg = info[0].As(); tns::Assert(IsGarbageCollectedWrapper(arg), isolate); ObjCDataWrapper* wrapper = ExtractWrapper(arg); id obj = wrapper->Data(); tns::Assert([obj isKindOfClass:[NSData class]], isolate); size_t length = [obj length]; void* data = const_cast([obj bytes]); std::unique_ptr backingStore = ArrayBuffer::NewBackingStore(data, length, [](void*, size_t, void*) { }, nullptr); Local result = ArrayBuffer::New(isolate, std::move(backingStore)); info.GetReturnValue().Set(result); }).ToLocal(&func); Isolate* isolate = context->GetIsolate(); tns::Assert(success, isolate); success = interop->Set(context, tns::ToV8String(isolate, "bufferFromData"), func).FromMaybe(false); tns::Assert(success, isolate); } void Interop::RegisterHandleOfFunction(Local context, Local interop) { Local func; bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); tns::Assert(info.Length() == 1, isolate); try { Local arg = info[0]; Local result = Interop::HandleOf(context, arg); if (result.IsEmpty()) { throw NativeScriptException("Unknown type"); } info.GetReturnValue().Set(result); } catch (NativeScriptException& ex) { ex.ReThrowToV8(isolate); } }).ToLocal(&func); Isolate* isolate = context->GetIsolate(); tns::Assert(success, isolate); success = interop->Set(context, tns::ToV8String(isolate, "handleof"), func).FromMaybe(false); tns::Assert(success, isolate); } void Interop::RegisterAllocFunction(Local context, Local interop) { Local func; bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); tns::Assert(info.Length() == 1, isolate); tns::Assert(tns::IsNumber(info[0]), isolate); Local context = isolate->GetCurrentContext(); Local arg = info[0].As(); int32_t value; tns::Assert(arg->Int32Value(context).To(&value), isolate); size_t size = static_cast(value); void* data = calloc(1, size); Local pointerInstance = Pointer::NewInstance(context, data); PointerWrapper* wrapper = ExtractWrapper(pointerInstance); wrapper->SetAdopted(true); info.GetReturnValue().Set(pointerInstance); }).ToLocal(&func); Isolate* isolate = context->GetIsolate(); tns::Assert(success, isolate); success = interop->Set(context, tns::ToV8String(isolate, "alloc"), func).FromMaybe(false); tns::Assert(success, isolate); } void Interop::RegisterFreeFunction(Local context, Local interop) { Local func; bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); tns::Assert(info.Length() == 1, isolate); Local arg = info[0]; BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); tns::Assert(wrapper->Type() == WrapperType::Pointer, isolate); PointerWrapper* pw = static_cast(wrapper); if (pw->IsAdopted()) { // TODO: throw an error that the pointer is adopted return; } if (pw->Data() != nullptr) { std::free(pw->Data()); } info.GetReturnValue().SetUndefined(); }).ToLocal(&func); Isolate* isolate = context->GetIsolate(); tns::Assert(success, isolate); success = interop->Set(context, tns::ToV8String(isolate, "free"), func).FromMaybe(false); tns::Assert(success, isolate); } void Interop::RegisterAdoptFunction(Local context, Local interop) { Local func; bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); tns::Assert(info.Length() == 1, isolate); Local arg = info[0]; BaseDataWrapper* wrapper = tns::GetValue(isolate, arg); tns::Assert(wrapper->Type() == WrapperType::Pointer, isolate); PointerWrapper* pw = static_cast(wrapper); pw->SetAdopted(true); info.GetReturnValue().Set(arg); }).ToLocal(&func); Isolate* isolate = context->GetIsolate(); tns::Assert(success, isolate); success = interop->Set(context, tns::ToV8String(isolate, "adopt"), func).FromMaybe(false); tns::Assert(success, isolate); } void Interop::RegisterSizeOfFunction(Local context, Local interop) { Local func; bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); tns::Assert(info.Length() == 1, isolate); try { Local arg = info[0]; size_t size = 0; if (!arg->IsNullOrUndefined()) { if (arg->IsObject()) { Local obj = arg.As(); if (BaseDataWrapper* wrapper = tns::GetValue(isolate, obj)) { switch (wrapper->Type()) { case WrapperType::ObjCClass: case WrapperType::ObjCProtocol: case WrapperType::ObjCObject: case WrapperType::PointerType: case WrapperType::Pointer: case WrapperType::Reference: case WrapperType::ReferenceType: case WrapperType::Block: case WrapperType::FunctionReference: case WrapperType::FunctionReferenceType: case WrapperType::Function: { size = sizeof(void*); break; } case WrapperType::Primitive: { PrimitiveDataWrapper* pw = static_cast(wrapper); size = pw->Size(); break; } case WrapperType::Struct: { StructWrapper* sw = static_cast(wrapper); size = sw->StructInfo().FFIType()->size; break; } case WrapperType::StructType: { StructTypeWrapper* sw = static_cast(wrapper); StructInfo structInfo = sw->StructInfo(); size = structInfo.FFIType()->size; break; } default: break; } } } } if (size == 0) { throw NativeScriptException("Unknown type"); } else { info.GetReturnValue().Set((double)size); } } catch (NativeScriptException& ex) { ex.ReThrowToV8(isolate); } }).ToLocal(&func); Isolate* isolate = context->GetIsolate(); tns::Assert(success, isolate); success = interop->Set(context, tns::ToV8String(isolate, "sizeof"), func).FromMaybe(false); tns::Assert(success, isolate); } const TypeEncoding* Interop::CreateEncoding(BinaryTypeEncodingType type) { TypeEncoding* typeEncoding = reinterpret_cast(calloc(1, sizeof(TypeEncoding))); typeEncoding->type = type; return typeEncoding; } Local Interop::HandleOf(Local context, Local value) { Isolate* isolate = context->GetIsolate(); if (!value->IsNullOrUndefined()) { if (value->IsArrayBuffer()) { Local buffer = value.As(); std::shared_ptr backingStore = buffer->GetBackingStore(); return Pointer::NewInstance(context, backingStore->Data()); } else if (value->IsArrayBufferView()) { Local bufferView = value.As(); std::shared_ptr backingStore = bufferView->Buffer()->GetBackingStore(); return Pointer::NewInstance(context, backingStore->Data()); } else if (value->IsObject()) { Local obj = value.As(); if (BaseDataWrapper* wrapper = tns::GetValue(isolate, obj)) { switch (wrapper->Type()) { case WrapperType::Primitive: { PrimitiveDataWrapper* pdw = static_cast(wrapper); void* handle = pdw; return Pointer::NewInstance(context, handle); } case WrapperType::ObjCClass: { ObjCClassWrapper* cw = static_cast(wrapper); @autoreleasepool { CFTypeRef ref = CFBridgingRetain(cw->Klass()); void* handle = const_cast(ref); CFRelease(ref); return Pointer::NewInstance(context, handle); } break; } case WrapperType::ObjCProtocol: { ObjCProtocolWrapper* pw = static_cast(wrapper); CFTypeRef ref = CFBridgingRetain(pw->Proto()); void* handle = const_cast(ref); CFRelease(ref); return Pointer::NewInstance(context, handle); } case WrapperType::ObjCObject: { ObjCDataWrapper* w = static_cast(wrapper); @autoreleasepool { id target = w->Data(); CFTypeRef ref = CFBridgingRetain(target); void* handle = const_cast(ref); CFRelease(ref); return Pointer::NewInstance(context, handle); } break; } case WrapperType::Struct: { StructWrapper* w = static_cast(wrapper); return Pointer::NewInstance(context, w->Data()); } case WrapperType::Reference: { ReferenceWrapper* w = static_cast(wrapper); if (w->HasValue()) { Local wrappedValue = w->Value().Get(isolate); if (tns::GetValue(isolate, wrappedValue) == nullptr) { return Pointer::NewInstance(context, w->UnsafeValue()); } return HandleOf(context, wrappedValue); } else if (w->Data() != nullptr) { return Pointer::NewInstance(context, w->Data()); } break; } case WrapperType::Pointer: { return value; } case WrapperType::Function: { FunctionWrapper* w = static_cast(wrapper); const FunctionMeta* meta = w->Meta(); void* handle = SymbolLoader::instance().loadFunctionSymbol(meta->topLevelModule(), meta->name()); return Pointer::NewInstance(context, handle); } case WrapperType::FunctionReference: { FunctionReferenceWrapper* w = static_cast(wrapper); if (w->Data() != nullptr) { return Pointer::NewInstance(context, w->Data()); } break; } case WrapperType::Block: { BlockWrapper* blockWrapper = static_cast(wrapper); return Pointer::NewInstance(context, blockWrapper->Block()); } default: break; } } } } else if (value->IsNull()) { return v8::Null(isolate); } return Local(); } }