#include "Reference.h" #include "Caches.h" #include "Constants.h" #include "Helpers.h" #include "Interop.h" #include "ObjectManager.h" #include "Pointer.h" using namespace v8; namespace tns { void Reference::Register(Local context, Local interop) { Isolate* isolate = v8::Isolate::GetCurrent(); Local ctorFunc = Reference::GetInteropReferenceCtorFunc(context); bool success = interop->Set(context, tns::ToV8String(isolate, "Reference"), ctorFunc) .FromMaybe(false); tns::Assert(success, isolate); } Local Reference::FromPointer(Local context, Local type, void* handle) { Isolate* isolate = v8::Isolate::GetCurrent(); Local pointer = Pointer::NewInstance(context, handle); ObjectManager::Register(context, pointer); Local interopReferenceCtorFunc = Reference::GetInteropReferenceCtorFunc(context); Local args[2] = {type, pointer}; Local instance; bool success = interopReferenceCtorFunc->NewInstance(context, 2, args) .ToLocal(&instance); tns::Assert(success, isolate); ObjectManager::Register(context, instance); return instance; } Local Reference::GetInteropReferenceCtorFunc( Local context) { Isolate* isolate = v8::Isolate::GetCurrent(); auto cache = Caches::Get(isolate); Persistent* interopReferenceCtor = cache->InteropReferenceCtorFunc.get(); if (interopReferenceCtor != nullptr) { return interopReferenceCtor->Get(isolate); } Local ctorFuncTemplate = FunctionTemplate::New(isolate, ReferenceConstructorCallback); ctorFuncTemplate->SetClassName(tns::ToV8String(isolate, "Reference")); ctorFuncTemplate->InstanceTemplate()->SetInternalFieldCount(1); ctorFuncTemplate->InstanceTemplate()->SetHandler( IndexedPropertyHandlerConfiguration(IndexedPropertyGetCallback, IndexedPropertySetCallback)); Local proto = ctorFuncTemplate->PrototypeTemplate(); proto->SetAccessorProperty(tns::ToV8String(isolate, "value"), FunctionTemplate::New(isolate, GetValueCallback), FunctionTemplate::New(isolate, SetValueCallback)); Local ctorFunc; if (!ctorFuncTemplate->GetFunction(context).ToLocal(&ctorFunc)) { tns::Assert(false, isolate); } tns::SetValue(isolate, ctorFunc, new ReferenceTypeWrapper()); Local prototypeValue; bool success = ctorFunc->Get(context, tns::ToV8String(isolate, "prototype")) .ToLocal(&prototypeValue); tns::Assert(success && prototypeValue->IsObject(), isolate); Local prototype = prototypeValue.As(); Reference::RegisterToStringMethod(context, prototype); cache->InteropReferenceCtorFunc = std::make_unique>(isolate, ctorFunc); cache->InteropReferenceCtorFunc->SetWrapperClassId( Constants::ClassTypes::DataWrapper); return ctorFunc; } void Reference::ReferenceConstructorCallback( const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); Persistent* val = nullptr; BaseDataWrapper* typeWrapper = nullptr; if (info.Length() == 1) { if (!info[0]->IsNullOrUndefined()) { val = new Persistent(isolate, info[0]); } } else if (info.Length() > 1) { if (!info[0]->IsNullOrUndefined() && !info[1]->IsNullOrUndefined()) { Local typeValue = info[0]; typeWrapper = tns::GetValue(isolate, typeValue); tns::Assert(typeWrapper != nullptr, isolate); val = new Persistent(isolate, info[1]); } } if (val != nullptr) { val->SetWeak(); } ReferenceWrapper* wrapper = new ReferenceWrapper(typeWrapper, val); Local thiz = info.This(); tns::SetValue(isolate, thiz, wrapper); ObjectManager::Register(context, thiz); } v8::Intercepted Reference::IndexedPropertyGetCallback( uint32_t index, const PropertyCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local thiz = info.Holder(); Local context = isolate->GetCurrentContext(); DataPair pair = Reference::GetDataPair(thiz); const TypeEncoding* typeEncoding = pair.typeEncoding_; size_t size = pair.size_; void* data = pair.data_; if (data == nullptr && typeEncoding == nullptr && pair.typeWrapper_ == nullptr) { // GetDataPair threw (released native object). return v8::Intercepted::kYes; } void* ptr = (uint8_t*)data + index * size; BaseCall call((uint8_t*)ptr); Local result; if (typeEncoding != nullptr) { result = Interop::GetResult(context, typeEncoding, &call, false); } else { result = Interop::GetResultByType(context, pair.typeWrapper_, &call); } info.GetReturnValue().Set(result); return v8::Intercepted::kYes; } v8::Intercepted Reference::IndexedPropertySetCallback( uint32_t index, Local value, const PropertyCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); Local thiz = info.Holder(); DataPair pair = Reference::GetDataPair(thiz); const TypeEncoding* typeEncoding = pair.typeEncoding_; size_t size = pair.size_; void* data = pair.data_; if (data == nullptr && typeEncoding == nullptr && pair.typeWrapper_ == nullptr) { // GetDataPair threw (released native object). return v8::Intercepted::kYes; } void* ptr = (uint8_t*)data + index * size; if (typeEncoding != nullptr) { Interop::WriteValue(context, typeEncoding, ptr, value); } else { Interop::WriteTypeValue(context, pair.typeWrapper_, ptr, value); } // Not intercepted: the native write is done, but V8 must still perform // the ordinary store, which is what the old void-returning callback did // by falling through without setting a return value. return v8::Intercepted::kNo; } void Reference::GetValueCallback(const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); Local result = Reference::GetReferredValue(context, info.This()); if (result.IsEmpty()) { info.GetReturnValue().SetUndefined(); } else { info.GetReturnValue().Set(result); } } void Reference::SetValueCallback(const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local value = info[0]; BaseDataWrapper* baseWrapper = tns::GetValueOrReport(isolate, info.This(), "Reference value set"); if (baseWrapper == nullptr) { return; } tns::Assert(baseWrapper->Type() == WrapperType::Reference, isolate); ReferenceWrapper* wrapper = static_cast(baseWrapper); Persistent* poValue = new Persistent(isolate, value); wrapper->SetData(nullptr); wrapper->SetEncoding(nullptr); wrapper->SetValue(poValue); } Local Reference::GetReferredValue(Local context, Local value) { Isolate* isolate = v8::Isolate::GetCurrent(); BaseDataWrapper* baseWrapper = tns::GetValue(isolate, value); if (baseWrapper == nullptr || baseWrapper->Type() != WrapperType::Reference) { return value; } ReferenceWrapper* wrapper = static_cast(baseWrapper); if (wrapper->Data() != nullptr && wrapper->Encoding() != nullptr) { const TypeEncoding* encoding = wrapper->Encoding(); uint8_t* data = (uint8_t*)wrapper->Data(); BaseCall call(data); Local jsResult = Interop::GetResult(context, encoding, &call, true); if (wrapper->Value() != nullptr) { wrapper->Value()->Reset(isolate, jsResult); } else { wrapper->SetValue(new Persistent(isolate, jsResult)); } wrapper->SetData(nullptr); wrapper->SetEncoding(nullptr); return jsResult; } if (wrapper->Value() == nullptr) { return Local(); } Local innerValue = wrapper->Value()->Get(isolate); baseWrapper = tns::GetValue(isolate, innerValue); if (baseWrapper != nullptr && baseWrapper->Type() == WrapperType::Reference) { wrapper = static_cast(baseWrapper); if (wrapper->Value() != nullptr) { return Reference::GetReferredValue(context, wrapper->Value()->Get(isolate)); } } BaseDataWrapper* typeWrapper = wrapper->TypeWrapper(); if (typeWrapper != nullptr && Reference::IsSupportedType(typeWrapper->Type()) && baseWrapper != nullptr && baseWrapper->Type() == WrapperType::Pointer) { Reference::DataPair pair = Reference::GetDataPair(value.As()); if (pair.data_ != nullptr) { BaseCall call((uint8_t*)pair.data_); Local result; if (pair.typeEncoding_ != nullptr) { result = Interop::GetResult(context, pair.typeEncoding_, &call, false); } else { result = Interop::GetResultByType(context, typeWrapper, &call); } return result; } } return innerValue; } void* Reference::GetWrappedPointer(Local context, Local reference, const TypeEncoding* typeEncoding) { if (reference.IsEmpty() || reference->IsNullOrUndefined()) { return nullptr; } Isolate* isolate = v8::Isolate::GetCurrent(); BaseDataWrapper* wrapper = tns::GetValueOrReport(isolate, reference, "Reference pointer access"); if (wrapper == nullptr) { return nullptr; } tns::Assert(wrapper->Type() == WrapperType::Reference, isolate); ReferenceWrapper* refWrapper = static_cast(wrapper); if (refWrapper->Data() != nullptr) { return refWrapper->Data(); } Local value = refWrapper->Value()->Get(isolate); BaseDataWrapper* wrappedValue = tns::GetValue(isolate, value); if (wrappedValue == nullptr) { BaseDataWrapper* typeWrapper = refWrapper->TypeWrapper(); if (typeWrapper == nullptr) { return nullptr; } if (typeWrapper->Type() == WrapperType::Primitive) { PrimitiveDataWrapper* primitiveWrapper = static_cast(typeWrapper); const TypeEncoding* enc = primitiveWrapper->TypeEncoding(); size_t size = primitiveWrapper->Size(); void* data = malloc(size); Interop::WriteValue(context, enc, data, value); refWrapper->SetData(data, true); return data; } if (typeWrapper->Type() != WrapperType::StructType) { return nullptr; } StructTypeWrapper* structTypeWrapper = static_cast(refWrapper->TypeWrapper()); StructInfo structInfo = structTypeWrapper->StructInfo(); void* data = malloc(structInfo.FFIType()->size); Interop::InitializeStruct(context, data, structInfo.Fields(), value); refWrapper->SetData(data, true); refWrapper->SetEncoding(typeEncoding); return data; } if (wrappedValue->Type() == WrapperType::ObjCClass) { ObjCClassWrapper* classWrapper = static_cast(wrappedValue); void* handle = malloc(sizeof(Class*)); Class clazz = classWrapper->Klass(); *static_cast(handle) = clazz; refWrapper->SetData(handle, true); return (Class**)handle; } if (wrappedValue->Type() == WrapperType::ObjCProtocol) { ObjCProtocolWrapper* protoWrapper = static_cast(wrappedValue); Protocol* proto = protoWrapper->Proto(); void* handle = malloc(sizeof(Protocol**)); *static_cast(handle) = proto; refWrapper->SetData(handle, true); return (Protocol**)handle; } if (wrappedValue->Type() == WrapperType::ObjCObject) { ObjCDataWrapper* dataWrapper = static_cast(wrappedValue); id target = dataWrapper->Data(); void* handle = malloc(sizeof(id)); *static_cast(handle) = target; refWrapper->SetData(handle, true); return (id)handle; } if (wrappedValue->Type() == WrapperType::Struct) { StructWrapper* structWrapper = static_cast(wrappedValue); return structWrapper->Data(); } if (wrappedValue->Type() == WrapperType::Pointer) { PointerWrapper* pw = static_cast(wrappedValue); void* data = pw->Data(); return data; } return nullptr; } void Reference::RegisterToStringMethod(Local context, Local prototype) { Isolate* isolate = v8::Isolate::GetCurrent(); Local funcTemplate = FunctionTemplate::New( isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); BaseDataWrapper* wrapper = tns::GetValueOrReport(isolate, info.This(), "Reference.toString"); if (wrapper == nullptr) { if (tns::GetReleasedObjectPolicy() == tns::ReleasedObjectPolicy::kReport) { info.GetReturnValue().Set( tns::ToV8String(isolate, "")); } return; } tns::Assert(wrapper->Type() == WrapperType::Reference, isolate); ReferenceWrapper* refWrapper = static_cast(wrapper); Persistent* value = refWrapper->Value(); char buffer[100]; if (value == nullptr) { snprintf(buffer, 100, "", reinterpret_cast(0)); } else { snprintf(buffer, 100, "", value); } Local result = tns::ToV8String(isolate, buffer); info.GetReturnValue().Set(result); }); Local func; tns::Assert(funcTemplate->GetFunction(context).ToLocal(&func), isolate); bool success = prototype->Set(context, tns::ToV8String(isolate, "toString"), func) .FromMaybe(false); tns::Assert(success, isolate); } Reference::DataPair Reference::GetDataPair(Local obj) { Local context; bool success = obj->GetCreationContext(v8::Isolate::GetCurrent()).ToLocal(&context); tns::Assert(success); Isolate* isolate = v8::Isolate::GetCurrent(); BaseDataWrapper* wrapper = tns::GetValueOrReport(isolate, obj, "Reference indexed access"); if (wrapper == nullptr) { return DataPair(nullptr, nullptr, nullptr, 0); } tns::Assert(wrapper->Type() == WrapperType::Reference, isolate); ReferenceWrapper* refWrapper = static_cast(wrapper); BaseDataWrapper* typeWrapper = refWrapper->TypeWrapper(); size_t size = 0; if (typeWrapper != nullptr) { const TypeEncoding* typeEncoding = nullptr; if (Reference::IsSupportedType(typeWrapper->Type())) { switch (typeWrapper->Type()) { case WrapperType::Primitive: { PrimitiveDataWrapper* primitiveWrapper = static_cast(typeWrapper); size = primitiveWrapper->Size(); typeEncoding = primitiveWrapper->TypeEncoding(); break; } case WrapperType::StructType: { StructTypeWrapper* structTypeWrapper = static_cast(refWrapper->TypeWrapper()); StructInfo structInfo = structTypeWrapper->StructInfo(); size = structInfo.FFIType()->size; break; } default: { break; } } } else { // TODO: Currently only PrimitiveDataWrappers and Structs are supported as // type parameters Objective C class classes should also be handled tns::Assert(false, isolate); } Local value = refWrapper->Value()->Get(isolate); BaseDataWrapper* wrappedValue = tns::GetValue(isolate, value); if (wrappedValue != nullptr && wrappedValue->Type() == WrapperType::Pointer) { PointerWrapper* pw = static_cast(wrappedValue); void* data = pw->Data(); DataPair pair(typeWrapper, typeEncoding, data, size); return pair; } } if (refWrapper->Encoding() != nullptr && refWrapper->Data() != nullptr) { const TypeEncoding* typeEncoding = refWrapper->Encoding(); if (typeWrapper == nullptr) { ffi_type* ffiType = FFICall::GetArgumentType(typeEncoding); size = ffiType->size; FFICall::DisposeFFIType(ffiType, typeEncoding); } DataPair pair(typeWrapper, typeEncoding, refWrapper->Data(), size); return pair; } tns::Assert(false, isolate); return DataPair(typeWrapper, nullptr, nullptr, size); } bool Reference::IsSupportedType(WrapperType type) { return type == WrapperType::Primitive || type == WrapperType::StructType; } } // namespace tns