#include "Pointer.h" #include "Caches.h" #include "Helpers.h" #include "NativeScriptException.h" #include "ObjectManager.h" #include "Constants.h" using namespace v8; namespace tns { void Pointer::Register(Local context, Local interop) { Isolate* isolate = context->GetIsolate(); Local ctorFunc = Pointer::GetPointerCtorFunc(context); bool success = interop->Set(context, tns::ToV8String(isolate, "Pointer"), ctorFunc).FromMaybe(false); tns::Assert(success, isolate); } Local Pointer::NewInstance(Local context, void* handle) { Isolate* isolate = context->GetIsolate(); intptr_t ptr = static_cast(reinterpret_cast(handle)); Local arg = BigInt::NewFromUnsigned(isolate, ptr); Local args[1] { arg }; Local result; Local ctorFunc = Pointer::GetPointerCtorFunc(context); bool success = ctorFunc->NewInstance(isolate->GetCurrentContext(), 1, args).ToLocal(&result); if (!success) { return v8::Undefined(isolate); } return result; } Local Pointer::GetPointerCtorFunc(Local context) { Isolate* isolate = context->GetIsolate(); auto cache = Caches::Get(isolate); Persistent* pointerCtorFunc = cache->PointerCtorFunc.get(); if (pointerCtorFunc != nullptr) { return pointerCtorFunc->Get(isolate); } Local ctorFuncTemplate = FunctionTemplate::New(isolate, PointerConstructorCallback); ctorFuncTemplate->InstanceTemplate()->SetInternalFieldCount(kMinGarbageCollectedEmbedderFields); ctorFuncTemplate->SetClassName(tns::ToV8String(isolate, "Pointer")); 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(); Pointer::RegisterAddMethod(context, prototype); Pointer::RegisterSubtractMethod(context, prototype); Pointer::RegisterToStringMethod(context, prototype); Pointer::RegisterToHexStringMethod(context, prototype); Pointer::RegisterToDecimalStringMethod(context, prototype); Pointer::RegisterToNumberMethod(context, prototype); Pointer::RegisterToBigIntMethod(context, prototype); cache->PointerCtorFunc = std::make_unique>(isolate, ctorFunc); cache->PointerCtorFunc->SetWrapperClassId(Constants::ClassTypes::DataWrapper); return ctorFunc; } void Pointer::PointerConstructorCallback(const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); try { void* ptr = nullptr; if (info.Length() == 1) { bool isBigInt = tns::IsBigInt(info[0]); bool isNumber = !isBigInt && tns::IsNumber(info[0]); if (!isBigInt && !isNumber) { throw NativeScriptException("Pointer constructor's first arg must be an integer."); } #if __SIZEOF_POINTER__ == 8 // JSC stores 64-bit integers as doubles in JSValue. // Caution: This means that pointers with more than 54 significant bits // are likely to be rounded and misrepresented! // However, current OS and hardware implementations are using 48 bits, // so we're safe at the time being. // See https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details // and https://en.wikipedia.org/wiki/ARM_architecture#ARMv8-A // The future is here, and turns out the OS is using more than 54 bits. // as a result, int64_t value; if (isBigInt) { value = info[0].As()->Int64Value(); } else { // TODO: Maybe log this? //#ifdef DEBUG // syslog(LOG_WARNING, "Using JS Number to represent a pointer. Result might be wrong."); //#endif tns::Assert(info[0].As()->IntegerValue(context).To(&value), isolate); } ptr = reinterpret_cast(value); #else int32_t value; if (isBigInt) { value = (int32_t)info[0].As()->Int64Value(); } else { tns::Assert(info[0].As()->Int32Value(context).To(&value), isolate); } ptr = reinterpret_cast(value); #endif } auto cache = Caches::Get(isolate); auto it = cache->PointerInstances.find(ptr); if (it != cache->PointerInstances.end()) { info.GetReturnValue().Set(it->second->Get(isolate)); return; } PointerWrapper* wrapper = MakeGarbageCollected(isolate, ptr); tns::SetValue(isolate, info.This(), wrapper); ObjectManager::Register(context, info.This()); cache->PointerInstances.emplace(ptr, std::make_shared>(isolate, info.This())); } catch (NativeScriptException& ex) { ex.ReThrowToV8(isolate); } } void Pointer::RegisterAddMethod(Local context, Local prototype) { Isolate* isolate = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); tns::Assert(info.Length() == 1 && tns::IsNumber(info[0]), isolate); PointerWrapper* wrapper = ExtractWrapper(info.This()); void* value = wrapper->Data(); int32_t offset; tns::Assert(info[0].As()->Int32Value(context).To(&offset), isolate); void* newValue = reinterpret_cast(reinterpret_cast(value) + offset); Local result = Pointer::NewInstance(context, newValue); info.GetReturnValue().Set(result); }); Local func; tns::Assert(funcTemplate->GetFunction(context).ToLocal(&func), isolate); bool success = prototype->Set(context, tns::ToV8String(isolate, "add"), func).FromMaybe(false); tns::Assert(success, isolate); } void Pointer::RegisterSubtractMethod(Local context, Local prototype) { Isolate* isolate = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); Local context = isolate->GetCurrentContext(); tns::Assert(info.Length() == 1 && tns::IsNumber(info[0]), isolate); PointerWrapper* wrapper = ExtractWrapper(info.This()); void* value = wrapper->Data(); int32_t offset; tns::Assert(info[0].As()->Int32Value(context).To(&offset), isolate); void* newValue = reinterpret_cast(reinterpret_cast(value) - offset); intptr_t newValuePtr = static_cast(reinterpret_cast(newValue)); Local ctorFunc = Pointer::GetPointerCtorFunc(context); Local arg = Number::New(isolate, newValuePtr); Local args[1] { arg }; Local result; bool success = ctorFunc->NewInstance(context, 1, args).ToLocal(&result); tns::Assert(success, isolate); info.GetReturnValue().Set(result); }); Local func; tns::Assert(funcTemplate->GetFunction(context).ToLocal(&func), isolate); bool success = prototype->Set(context, tns::ToV8String(isolate, "subtract"), func).FromMaybe(false); tns::Assert(success, isolate); } void Pointer::RegisterToStringMethod(Local context, Local prototype) { Isolate* isolate = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); PointerWrapper* wrapper = ExtractWrapper(info.This()); void* value = wrapper->Data(); char buffer[100]; 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); } void Pointer::RegisterToHexStringMethod(Local context, Local prototype) { Isolate* isolate = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); PointerWrapper* wrapper = ExtractWrapper(info.This()); const void* value = wrapper->Data(); char buffer[100]; snprintf(buffer, 100, "%p", 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, "toHexString"), func).FromMaybe(false); tns::Assert(success, isolate); } void Pointer::RegisterToDecimalStringMethod(Local context, Local prototype) { Isolate* isolate = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); PointerWrapper* wrapper = ExtractWrapper(info.This()); const void* value = wrapper->Data(); intptr_t ptr = static_cast(reinterpret_cast(value)); char buffer[100]; snprintf(buffer, 100, "%ld", ptr); 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, "toDecimalString"), func).FromMaybe(false); tns::Assert(success, isolate); } void Pointer::RegisterToNumberMethod(Local context, Local prototype) { Isolate* isolate = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); PointerWrapper* wrapper = ExtractWrapper(info.This()); const void* value = wrapper->Data(); size_t number = reinterpret_cast(value); Local result = Number::New(isolate, number); info.GetReturnValue().Set(result); }); Local func; tns::Assert(funcTemplate->GetFunction(context).ToLocal(&func), isolate); bool success = prototype->Set(context, tns::ToV8String(isolate, "toNumber"), func).FromMaybe(false); tns::Assert(success, isolate); } void Pointer::RegisterToBigIntMethod(Local context, Local prototype) { Isolate* isolate = context->GetIsolate(); Local funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); PointerWrapper* wrapper = ExtractWrapper(info.This()); const void* value = wrapper->Data(); size_t number = reinterpret_cast(value); Local result = BigInt::NewFromUnsigned(isolate, number); info.GetReturnValue().Set(result); }); Local func; tns::Assert(funcTemplate->GetFunction(context).ToLocal(&func), isolate); bool success = prototype->Set(context, tns::ToV8String(isolate, "toBigInt"), func).FromMaybe(false); tns::Assert(success, isolate); } }