#include "Reference.h" #include "Pointer.h" #include "Caches.h" #include "ObjectManager.h" #include "Helpers.h" #include "Interop.h" #include "Constants.h" using namespace v8; namespace tns { void Reference::Register(Local context, Local interop) { Isolate* isolate = context->GetIsolate(); 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 = context->GetIsolate(); 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 = context->GetIsolate(); 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(kMinGarbageCollectedEmbedderFields); ctorFuncTemplate->InstanceTemplate()->SetHandler(IndexedPropertyHandlerConfiguration(IndexedPropertyGetCallback, IndexedPropertySetCallback)); Local proto = ctorFuncTemplate->PrototypeTemplate(); proto->SetAccessor(tns::ToV8String(isolate, "value"), GetValueCallback, SetValueCallback); Local ctorFunc; if (!ctorFuncTemplate->GetFunction(context).ToLocal(&ctorFunc)) { tns::Assert(false, isolate); } tns::SetValue(isolate, ctorFunc, MakeGarbageCollected(isolate)); 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(); TracedValue* value = nullptr; BaseDataWrapper* typeWrapper = nullptr; if (info.Length() == 1) { if (!info[0]->IsNullOrUndefined()) { value = new TracedValue(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); value = new TracedValue(isolate, info[1]); } } tns::SetValue(isolate, info.This(), MakeGarbageCollected(isolate, typeWrapper, value)); } void Reference::IndexedPropertyGetCallback(uint32_t index, const PropertyCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local thiz = info.This(); Local context = isolate->GetCurrentContext(); DataPair pair = Reference::GetTypeEncodingDataPair(thiz); const TypeEncoding* typeEncoding = pair.typeEncoding_; size_t size = pair.size_; void* data = pair.data_; void* ptr = (uint8_t*)data + index * size; BaseCall call((uint8_t*)ptr); Local result = Interop::GetResult(context, typeEncoding, &call, false); info.GetReturnValue().Set(result); } void Reference::IndexedPropertySetCallback(uint32_t index, Local value, const PropertyCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); Local thiz = info.This(); DataPair pair = Reference::GetTypeEncodingDataPair(thiz); const TypeEncoding* typeEncoding = pair.typeEncoding_; size_t size = pair.size_; void* data = pair.data_; void* ptr = (uint8_t*)data + index * size; Interop::WriteValue(context, typeEncoding, ptr, value); } void Reference::GetValueCallback(Local name, const PropertyCallbackInfo& 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(Local name, Local value, const PropertyCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); BaseDataWrapper* baseWrapper = tns::GetValue(isolate, info.This()); tns::Assert(baseWrapper->Type() == WrapperType::Reference, isolate); ReferenceWrapper* wrapper = static_cast(baseWrapper); wrapper->SetData(nullptr); wrapper->SetEncoding(nullptr); wrapper->SetValue(new TracedValue(ToTraced(isolate, value))); } Local Reference::GetReferredValue(Local context, Local value) { Isolate* isolate = context->GetIsolate(); 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); wrapper->SetValue(new TracedValue(ToTraced(isolate, jsResult))); wrapper->SetData(nullptr); wrapper->SetEncoding(nullptr); return jsResult; } if (!wrapper->HasValue()) { 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().IsEmpty()) { return Reference::GetReferredValue(context, wrapper->Value().Get(isolate)); } } BaseDataWrapper* typeWrapper = wrapper->TypeWrapper(); if (typeWrapper != nullptr && typeWrapper->Type() == WrapperType::Primitive && baseWrapper != nullptr && baseWrapper->Type() == WrapperType::Pointer) { Reference::DataPair pair = GetTypeEncodingDataPair(value.As()); if (pair.data_ != nullptr && pair.typeEncoding_ != nullptr) { BaseCall call((uint8_t*)pair.data_); Local result = Interop::GetResult(context, pair.typeEncoding_, &call, false); return result; } } return innerValue; } void* Reference::GetWrappedPointer(Local context, Local reference, const TypeEncoding* typeEncoding) { if (reference.IsEmpty() || reference->IsNullOrUndefined()) { return nullptr; } Isolate* isolate = context->GetIsolate(); BaseDataWrapper* wrapper = tns::GetValue(isolate, reference); tns::Assert(wrapper != nullptr && 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 = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); BaseDataWrapper* wrapper = tns::GetValue(isolate, info.This()); tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::Reference, isolate); ReferenceWrapper* refWrapper = static_cast(wrapper); Local value = refWrapper->Value().Get(isolate); char buffer[100]; if (value.IsEmpty()) { snprintf(buffer, 100, "", reinterpret_cast(0)); } else { // FIXME: this is pretty goofy - but the assumption is that the Address* field is the first field of the // Local object, and the Address is a pointer to a pointer in the v8 Heap, so the value should be stable. void* pointer = *reinterpret_cast(&value); snprintf(buffer, 100, "", pointer); } 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::GetTypeEncodingDataPair(Local obj) { Local context; bool success = obj->GetCreationContext().ToLocal(&context); tns::Assert(success); Isolate* isolate = context->GetIsolate(); BaseDataWrapper* wrapper = tns::GetValue(isolate, obj); tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::Reference, isolate); ReferenceWrapper* refWrapper = static_cast(wrapper); BaseDataWrapper* typeWrapper = refWrapper->TypeWrapper(); if (typeWrapper == nullptr) { // TODO: Missing type when creating the Reference instance tns::Assert(false, isolate); } if (typeWrapper->Type() != WrapperType::Primitive) { // TODO: Currently only PrimitiveDataWrappers are supported as type parameters // Objective C class classes and structures should also be handled tns::Assert(false, isolate); } PrimitiveDataWrapper* primitiveWrapper = static_cast(typeWrapper); Local value = refWrapper->Value().Get(isolate); BaseDataWrapper* wrappedValue = tns::GetValue(isolate, value); if (wrappedValue != nullptr && wrappedValue->Type() == WrapperType::Pointer) { const TypeEncoding* typeEncoding = primitiveWrapper->TypeEncoding(); PointerWrapper* pw = static_cast(wrappedValue); void* data = pw->Data(); DataPair pair(typeEncoding, data, primitiveWrapper->Size()); return pair; } if (refWrapper->Encoding() != nullptr && refWrapper->Data() != nullptr) { DataPair pair(refWrapper->Encoding(), refWrapper->Data(), primitiveWrapper->Size()); return pair; } tns::Assert(false, isolate); return DataPair(nullptr, nullptr, 0); } }