/* * Tencent is pleased to support the open source community by making Puerts available. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. * Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may * be subject to their corresponding license terms. This file is subject to the terms and conditions defined in file 'LICENSE', * which is part of this source code package. */ #pragma once #include #include #include #include "DataTransfer.h" #include "JSClassRegister.h" #define __DefObjectType_v8_impl(CLS) \ namespace PUERTS_NAMESPACE \ { \ template <> \ struct is_objecttype : public std::true_type \ { \ }; \ } #define __DefCDataPointerConverter_v8_impl(CLS) \ namespace PUERTS_NAMESPACE \ { \ namespace v8_impl \ { \ template <> \ struct Converter \ { \ static v8::Local toScript(v8::Local context, CLS* value) \ { \ return ::PUERTS_NAMESPACE::DataTransfer::FindOrAddCData( \ context->GetIsolate(), context, DynamicTypeId::get(value), value, true); \ } \ static CLS* toCpp(v8::Local context, const v8::Local& value) \ { \ return ::PUERTS_NAMESPACE::DataTransfer::GetPointerFast(value.As()); \ } \ static bool accept(v8::Local context, const v8::Local& value) \ { \ return ::PUERTS_NAMESPACE::DataTransfer::IsInstanceOf( \ context->GetIsolate(), StaticTypeId::get(), value.As()); \ } \ }; \ } \ } #define __DefCDataConverter_v8_impl(CLS) \ namespace PUERTS_NAMESPACE \ { \ namespace v8_impl \ { \ template <> \ struct Converter \ { \ static v8::Local toScript(v8::Local context, CLS value) \ { \ return ::PUERTS_NAMESPACE::DataTransfer::FindOrAddCData( \ context->GetIsolate(), context, DynamicTypeId::get(&value), new CLS(value), false); \ } \ static CLS toCpp(v8::Local context, const v8::Local& value) \ { \ return *::PUERTS_NAMESPACE::DataTransfer::GetPointerFast(value.As()); \ } \ static bool accept(v8::Local context, const v8::Local& value) \ { \ return ::PUERTS_NAMESPACE::DataTransfer::IsInstanceOf( \ context->GetIsolate(), StaticTypeId::get(), value.As()); \ } \ }; \ } \ } namespace PUERTS_NAMESPACE { namespace v8_impl { template struct Converter; template struct CustomArgumentBufferType { static constexpr bool enable = false; }; struct API { typedef const v8::FunctionCallbackInfo& CallbackInfoType; typedef v8::Local ContextType; typedef v8::Local ValueType; typedef v8::FunctionCallback FunctionCallbackType; typedef void* (*InitializeFuncType)(const v8::FunctionCallbackInfo& Info); typedef void (*FinalizeFuncType)(void* Ptr, void* ClassData, void* EnvData); typedef JSFunctionInfo GeneralFunctionInfo; typedef JSPropertyInfo GeneralPropertyInfo; typedef NamedFunctionInfo GeneralFunctionReflectionInfo; typedef NamedPropertyInfo GeneralPropertyReflectionInfo; V8_INLINE static int GetArgsLen(const v8::FunctionCallbackInfo& info) { return info.Length(); } V8_INLINE static v8::Local GetArg(const v8::FunctionCallbackInfo& info, int index) { return info[index]; } V8_INLINE static v8::Local GetContext(const v8::FunctionCallbackInfo& info) { return info.GetIsolate()->GetCurrentContext(); } V8_INLINE static v8::Local GetThis(const v8::FunctionCallbackInfo& info) { return info.This(); } V8_INLINE static v8::Local GetHolder(const v8::FunctionCallbackInfo& info) { return info.Holder(); } // use where GetSelfFromData is true V8_INLINE static void* GetFunctionData(const v8::FunctionCallbackInfo& info) { return v8::Local::Cast(info.Data())->Value(); } V8_INLINE static void ThrowException(const v8::FunctionCallbackInfo& info, const char* msg) { v8::Isolate* isolate = info.GetIsolate(); isolate->ThrowException( v8::Exception::Error(v8::String::NewFromUtf8(isolate, msg, v8::NewStringType::kNormal).ToLocalChecked())); } V8_INLINE static void SetReturn(const v8::FunctionCallbackInfo& info, v8::Local value) { info.GetReturnValue().Set(value); } template V8_INLINE static void LinkOuter(v8::Local Context, v8::Local Outer, v8::Local Inner) { LinkOuterImpl(Context, Outer, Inner); } V8_INLINE static void UpdateRefValue(v8::Local context, v8::Local holder, v8::Local value) { if (holder->IsObject()) { auto outer = holder->ToObject(context).ToLocalChecked(); (void) (outer->Set(context, 0, value)); } } template V8_INLINE static T* FastGetNativeObjectPointer(v8::Local context, v8::Local Object) { return DataTransfer::GetPointerFast(Object); } V8_INLINE static v8::Local GetUndefined(v8::Local context) { return v8::Undefined(context->GetIsolate()); } V8_INLINE static bool IsNullOrUndefined(v8::Local context, v8::Local val) { return val->IsNullOrUndefined(); } template static void Register(FinalizeFuncType Finalize, const CDB& Cdb) { const bool isUEType = is_uetype::value; static std::vector s_functions_{}; static std::vector s_methods_{}; static std::vector s_properties_{}; static std::vector s_variables_{}; static std::vector s_constructorInfos_{}; static std::vector s_methodInfos_{}; static std::vector s_functionInfos_{}; static std::vector s_propertyInfos_{}; static std::vector s_variableInfos_{}; JSClassDefinition ClassDef = JSClassEmptyDefinition; if (isUEType) { ClassDef.UETypeName = Cdb.className_; } else { ClassDef.ScriptName = Cdb.className_; ClassDef.TypeId = StaticTypeId::get(); ClassDef.SuperTypeId = Cdb.superTypeId_; } ClassDef.SetInitialize(Cdb.constructor_); ClassDef.Finalize = Finalize; s_functions_ = std::move(Cdb.functions_); s_functions_.push_back(JSFunctionInfo()); ClassDef.Functions = s_functions_.data(); s_methods_ = std::move(Cdb.methods_); s_methods_.push_back(JSFunctionInfo()); ClassDef.Methods = s_methods_.data(); s_properties_ = std::move(Cdb.properties_); s_properties_.push_back(JSPropertyInfo()); ClassDef.Properties = s_properties_.data(); s_variables_ = std::move(Cdb.variables_); s_variables_.push_back(JSPropertyInfo()); ClassDef.Variables = s_variables_.data(); s_constructorInfos_ = std::move(Cdb.constructorInfos_); s_constructorInfos_.push_back(NamedFunctionInfo{nullptr, nullptr}); ClassDef.ConstructorInfos = s_constructorInfos_.data(); s_methodInfos_ = std::move(Cdb.methodInfos_); s_methodInfos_.push_back(NamedFunctionInfo{nullptr, nullptr}); ClassDef.MethodInfos = s_methodInfos_.data(); s_functionInfos_ = std::move(Cdb.functionInfos_); s_functionInfos_.push_back(NamedFunctionInfo{nullptr, nullptr}); ClassDef.FunctionInfos = s_functionInfos_.data(); s_propertyInfos_ = std::move(Cdb.propertyInfos_); s_propertyInfos_.push_back(NamedPropertyInfo{nullptr, nullptr}); ClassDef.PropertyInfos = s_propertyInfos_.data(); s_variableInfos_ = std::move(Cdb.variableInfos_); s_variableInfos_.push_back(NamedPropertyInfo{nullptr, nullptr}); ClassDef.VariableInfos = s_variableInfos_.data(); RegisterJSClass(ClassDef); } template using Converter = Converter; template using CustomArgumentBufferType = CustomArgumentBufferType; }; class StringHolder { public: StringHolder(v8::Local context, const v8::Local value) { needFree = false; if (value->IsArrayBufferView()) { v8::Local BuffView = value.As(); auto Ab = BuffView->Buffer(); data = static_cast(DataTransfer::GetArrayBufferData(Ab)) + BuffView->ByteOffset(); } else if (value->IsArrayBuffer()) { auto Ab = v8::Local::Cast(value); data = static_cast(DataTransfer::GetArrayBufferData(Ab)); } else { if (value.IsEmpty()) return; const auto isolate = context->GetIsolate(); v8::TryCatch try_catch(isolate); v8::Local str; if (!value->ToString(context).ToLocal(&str)) return; const int length = str->Utf8Length(isolate); data = new char[length + 1]; str->WriteUtf8(isolate, data); needFree = true; } } ~StringHolder() { if (needFree && data) { delete[] data; } } const char* Data() const { return data; } // Disallow copying and assigning. StringHolder(const StringHolder&) = delete; void operator=(const StringHolder&) = delete; private: char* data; bool needFree; }; template <> struct CustomArgumentBufferType { using type = StringHolder; static constexpr bool enable = true; }; template struct Converter::value && sizeof(T) == 8 && std::is_signed::value>::type> { static v8::Local toScript(v8::Local context, T value) { return v8::BigInt::New(context->GetIsolate(), value); } static T toCpp(v8::Local context, const v8::Local& value) { return value->IsBigInt() ? static_cast(value->ToBigInt(context).ToLocalChecked()->Int64Value()) : 0; } static bool accept(v8::Local context, const v8::Local& value) { return value->IsBigInt(); } }; template struct Converter::value && sizeof(T) == 8 && !std::is_signed::value>::type> { static v8::Local toScript(v8::Local context, T value) { return v8::BigInt::NewFromUnsigned(context->GetIsolate(), value); } static T toCpp(v8::Local context, const v8::Local& value) { return value->IsBigInt() ? static_cast(value->ToBigInt(context).ToLocalChecked()->Uint64Value()) : 0; } static bool accept(v8::Local context, const v8::Local& value) { return value->IsBigInt(); } }; template struct Converter::value && sizeof(T) < 8 && std::is_signed::value>::type> { static v8::Local toScript(v8::Local context, T value) { return v8::Integer::New(context->GetIsolate(), value); } static T toCpp(v8::Local context, const v8::Local& value) { return static_cast(value->Int32Value(context).ToChecked()); } static bool accept(v8::Local context, const v8::Local& value) { return value->IsInt32(); } }; template struct Converter::value && sizeof(T) < 8 && !std::is_signed::value>::type> { static v8::Local toScript(v8::Local context, T value) { return v8::Integer::NewFromUnsigned(context->GetIsolate(), value); } static T toCpp(v8::Local context, const v8::Local& value) { return static_cast(value->Uint32Value(context).ToChecked()); } static bool accept(v8::Local context, const v8::Local& value) { return value->IsUint32(); } }; template struct Converter::value>::type> { static v8::Local toScript(v8::Local context, T value) { return v8::Integer::New(context->GetIsolate(), static_cast(value)); } static T toCpp(v8::Local context, const v8::Local& value) { return static_cast(value->Int32Value(context).ToChecked()); } static bool accept(v8::Local context, const v8::Local& value) { return value->IsInt32(); } }; template struct Converter::value>::type> { static v8::Local toScript(v8::Local context, T value) { return v8::Number::New(context->GetIsolate(), value); } static T toCpp(v8::Local context, const v8::Local& value) { return static_cast(value->NumberValue(context).ToChecked()); } static bool accept(v8::Local context, const v8::Local& value) { return value->IsNumber(); } }; template <> struct Converter { static v8::Local toScript(v8::Local context, std::string value) { return v8::String::NewFromUtf8(context->GetIsolate(), value.c_str(), v8::NewStringType::kNormal).ToLocalChecked(); } static std::string toCpp(v8::Local context, const v8::Local& value) { return *v8::String::Utf8Value(context->GetIsolate(), value); } static bool accept(v8::Local context, const v8::Local& value) { return value->IsString(); } }; template <> struct Converter { static v8::Local toScript(v8::Local context, const char* value) { return v8::String::NewFromUtf8(context->GetIsolate(), value, v8::NewStringType::kNormal).ToLocalChecked(); } static bool accept(v8::Local context, const v8::Local& value) { return value->IsString(); } }; template <> struct Converter { static v8::Local toScript(v8::Local context, void* value) { return DataTransfer::NewArrayBuffer(context, value, 0); } static void* toCpp(v8::Local context, const v8::Local& value) { if (value->IsArrayBufferView()) { v8::Local BuffView = value.As(); auto Ab = BuffView->Buffer(); return static_cast(DataTransfer::GetArrayBufferData(Ab)) + BuffView->ByteOffset(); } if (value->IsArrayBuffer()) { auto Ab = v8::Local::Cast(value); return DataTransfer::GetArrayBufferData(Ab); } if (value->IsObject()) { return DataTransfer::GetPointerFast(value.As()); } return nullptr; } static bool accept(v8::Local context, const v8::Local& value) { return value->IsArrayBuffer() || value->IsArrayBufferView(); } }; template <> struct Converter { static v8::Local toScript(v8::Local context, bool value) { return v8::Boolean::New(context->GetIsolate(), value); } static bool toCpp(v8::Local context, const v8::Local& value) { return value->BooleanValue(context->GetIsolate()); } static bool accept(v8::Local context, const v8::Local& value) { return value->IsBoolean(); } }; template struct Converter, typename std::enable_if::value && !is_uetype::value>::type> { static v8::Local toScript(v8::Local context, const T& value) { auto result = v8::Object::New(context->GetIsolate()); auto _unused = result->Set(context, 0, Converter::toScript(context, value)); return result; } static T toCpp(v8::Local context, const v8::Local& value) { if (value->IsObject()) { auto outer = value->ToObject(context).ToLocalChecked(); auto realvalue = outer->Get(context, 0).ToLocalChecked(); return Converter::toCpp(context, realvalue); } return {}; } static bool accept(v8::Local context, const v8::Local& value) { return value->IsObject(); // do not checked inner } }; template struct Converter, typename std::enable_if::value || is_uetype::value>::type> { static v8::Local toScript(v8::Local context, const T& value) { auto result = v8::Object::New(context->GetIsolate()); auto _unused = result->Set(context, 0, Converter::toScript(context, value)); return result; } static T* toCpp(v8::Local context, const v8::Local& value) { if (!value.IsEmpty() && value->IsObject()) { auto outer = value->ToObject(context).ToLocalChecked(); auto realvalue = outer->Get(context, 0).ToLocalChecked(); return Converter::type*>::toCpp(context, realvalue); } return nullptr; } static bool accept(v8::Local context, const v8::Local& value) { return value->IsObject(); // do not checked inner } }; template struct Converter::type>::value && !std::is_array::value && !std::is_const::type>::value && std::is_pointer::value>::type> { static v8::Local toScript(v8::Local context, T value) { return DataTransfer::NewArrayBuffer(context, value, 0); } static T toCpp(v8::Local context, const v8::Local& value) { if (value->IsArrayBufferView()) { v8::Local BuffView = value.As(); auto Ab = BuffView->Buffer(); return reinterpret_cast(static_cast(DataTransfer::GetArrayBufferData(Ab)) + BuffView->ByteOffset()); } if (value->IsArrayBuffer()) { auto Ab = v8::Local::Cast(value); return static_cast(DataTransfer::GetArrayBufferData(Ab)); } return nullptr; } static bool accept(v8::Local context, const v8::Local& value) { return value->IsArrayBuffer() || value->IsArrayBufferView(); } }; template struct Converter::value && !std::is_const::value>::type> { static v8::Local toScript(v8::Local context, T value[Size]) { return DataTransfer::NewArrayBuffer(context, &(value[0]), sizeof(T) * Size); } static bool accept(v8::Local context, const v8::Local& value) { if (value->IsArrayBufferView()) { v8::Local buffView = value.As(); return buffView->ByteLength() >= sizeof(T) * Size; } if (value->IsArrayBuffer()) { auto ab = v8::Local::Cast(value); size_t byteLength; (void) (DataTransfer::GetArrayBufferData(ab, byteLength)); return byteLength >= sizeof(T) * Size; } return false; } }; template struct Converter::value && std::is_constructible::value && is_objecttype::value && !is_uetype::value>::type> { static v8::Local toScript(v8::Local context, T value) { return DataTransfer::FindOrAddCData(context->GetIsolate(), context, DynamicTypeId::get(&value), new T(value), false); } static T toCpp(v8::Local context, const v8::Local& value) { T* ptr = DataTransfer::GetPointerFast(value.As()); return ptr ? *ptr : T{}; } static bool accept(v8::Local context, const v8::Local& value) { return DataTransfer::IsInstanceOf(context->GetIsolate(), StaticTypeId::get(), value.As()); } }; template struct Converter::value || std::is_same::value || is_script_type::value) && !is_uetype::value && !std::is_same::value>::type> { static v8::Local toScript(v8::Local context, const T* value) { return Converter::toScript(context, const_cast(value)); } static const T* toCpp(v8::Local context, const v8::Local& value) { return Converter::toCpp(context, value); } static bool accept(v8::Local context, const v8::Local& value) { return Converter::accept(context, value); } }; } // namespace v8_impl } // namespace PUERTS_NAMESPACE