diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f08014f5..5bd84ec6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,12 @@ name: CI on: push: - branches: '*' + branches: [main] pull_request: jobs: build: - runs-on: macos-latest + runs-on: macos-26 steps: - uses: actions/checkout@v5 with: @@ -20,6 +20,9 @@ jobs: - name: Install Dependencies run: npm install + - name: Download V8 + run: ./download_v8.sh + - name: Install libffi build tools run: brew install autoconf automake libtool texinfo @@ -28,6 +31,22 @@ jobs: - name: Build metadata generator run: npm run build-metagen + + - name: Generate macOS metadata + run: npm run metagen macos - name: Build NativeScript - run: npm run build --quickjs + run: npm run build + + - name: Build CLI + run: npm run build:macos-cli + + - name: Run Tests on macOS + run: npm run test:macos + + - name: Run Tests on iOS + env: + IOS_BUILD_TIMEOUT_MS: "600000" + IOS_TEST_TIMEOUT_MS: "600000" + IOS_TEST_INACTIVITY_TIMEOUT_MS: "180000" + run: npm run test:ios diff --git a/.gitignore b/.gitignore index fba61512..25430b89 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,10 @@ v8_build .cache/ +dist +packages/*/types + SwiftBindgen + +# Generated Objective-C/C dispatch wrappers +NativeScript/ffi/GeneratedSignatureDispatch.inc diff --git a/NativeScript/CMakeLists.txt b/NativeScript/CMakeLists.txt index 938c3cb4..417679ff 100644 --- a/NativeScript/CMakeLists.txt +++ b/NativeScript/CMakeLists.txt @@ -78,7 +78,6 @@ elseif(TARGET_ENGINE STREQUAL "hermes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++20 -DTARGET_ENGINE_HERMES") elseif(TARGET_ENGINE STREQUAL "v8") set(TARGET_ENGINE_V8 TRUE) - add_link_options("-fuse-ld=lld") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -stdlib=libc++ -std=c++20 -DTARGET_ENGINE_V8") elseif(TARGET_ENGINE STREQUAL "quickjs") set(TARGET_ENGINE_QUICKJS TRUE) @@ -152,10 +151,16 @@ if(ENABLE_JS_RUNTIME) runtime/modules/worker/WorkerImpl.mm runtime/modules/worker/WorkerImpl.mm runtime/modules/module/ModuleInternal.cpp + runtime/modules/node/Node.cpp + runtime/modules/node/FS.cpp + runtime/modules/node/Path.cpp + runtime/modules/node/Process.cpp runtime/modules/performance/Performance.cpp + runtime/ThreadSafeFunction.mm runtime/Bundle.mm runtime/modules/timers/Timers.mm runtime/modules/app/App.mm + runtime/modules/web/Web.mm runtime/NativeScript.mm runtime/RuntimeConfig.cpp runtime/modules/url/ada/ada.cpp @@ -166,7 +171,6 @@ if(ENABLE_JS_RUNTIME) if(TARGET_ENGINE_V8) include_directories( napi/v8 - napi/v8/include napi/v8/v8_inspector ) @@ -292,6 +296,17 @@ target_sources( "NativeScript.h" ) +if(TARGET_ENGINE_V8 AND TARGET_PLATFORM_IOS) + # iOS V8 slices are built with pointer compression enabled. Keep embedder + # build flags in sync to satisfy V8::Initialize() build config checks. + target_compile_definitions( + ${NAME} + PRIVATE + V8_COMPRESS_POINTERS + V8_31BIT_SMIS_ON_64BIT_ARCH + ) +endif() + set(FRAMEWORK_VERSION_VALUE "${VERSION}") if(TARGET_PLATFORM_MACOS) # macOS framework consumers (including Xcode's copy/sign phases) expect @@ -392,27 +407,42 @@ if(TARGET_ENGINE_JSC) endif() if(TARGET_ENGINE_V8) - if(TARGET_PLATFORM_MACOS) - target_link_directories( - ${NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../Frameworks/v8_macos - ) + add_library(v8::monolith STATIC IMPORTED GLOBAL) + + set(V8_XCFRAMEWORK "${CMAKE_SOURCE_DIR}/../Frameworks/libv8_monolith.xcframework") + + # Decide platform + slice + if(APPLE) + if(TARGET_PLATFORM STREQUAL "ios-sim") + # Prefer universal sim slice if present + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/ios-arm64-simulator/libv8_monolith.framework") + if(NOT EXISTS "${V8_SLICE_DIR}") + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/ios-arm64-simulator/libv8_monolith.framework") # fallback + endif() + elseif(TARGET_PLATFORM STREQUAL "ios") + # Prefer universal sim slice if present + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/ios-arm64/libv8_monolith.framework") + if(NOT EXISTS "${V8_SLICE_DIR}") + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/ios-arm64/libv8_monolith.framework") # fallback + endif() + else() + # macOS + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/macos-arm64/libv8_monolith.framework") + if(NOT EXISTS "${V8_SLICE_DIR}") + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/macos-arm64/libv8_monolith.framework") # keep same, just explicit + endif() + endif() else() - target_link_directories( - ${NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../Frameworks/v8_ios - ) + message(FATAL_ERROR "This example is for Apple platforms only.") endif() - target_link_libraries( - ${NAME} - PRIVATE - "v8_monolith" - "v8_libbase" - "v8_libplatform" + # Point imported lib at the slice + set_target_properties(v8::monolith PROPERTIES + IMPORTED_LOCATION "${V8_SLICE_DIR}/libv8_monolith" + INTERFACE_INCLUDE_DIRECTORIES "${V8_SLICE_DIR}/Headers" ) + + target_link_libraries(${NAME} PRIVATE v8::monolith) endif() target_link_libraries( diff --git a/NativeScript/cli/main.cpp b/NativeScript/cli/main.cpp index 0c1c98f3..5f7a6c35 100644 --- a/NativeScript/cli/main.cpp +++ b/NativeScript/cli/main.cpp @@ -61,6 +61,13 @@ void bootFromModuleSpec(std::string baseDir, std::string spec) { int main(int argc, char** argv) { RuntimeConfig.LogToSystemConsole = true; + RuntimeConfig.Arguments.clear(); + if (argv != nullptr && argc > 0) { + RuntimeConfig.Arguments.reserve(static_cast(argc)); + for (int i = 0; i < argc; i++) { + RuntimeConfig.Arguments.emplace_back(argv[i] != nullptr ? argv[i] : ""); + } + } #ifdef __APPLE__ std::string bytecodePath = getBytecodePathFromBundle(); diff --git a/NativeScript/ffi/Block.h b/NativeScript/ffi/Block.h index f1ae8b50..97097831 100644 --- a/NativeScript/ffi/Block.h +++ b/NativeScript/ffi/Block.h @@ -14,9 +14,12 @@ class FunctionPointer { void* function; metagen::MDSectionOffset offset; Cif* cif; + bool ownsCif = false; static napi_value wrap(napi_env env, void* function, metagen::MDSectionOffset offset, bool isBlock); + static napi_value wrapWithEncoding(napi_env env, void* function, + const char* encoding, bool isBlock); static void finalize(napi_env env, void* finalize_data, void* finalize_hint); static napi_value jsCallAsCFunction(napi_env env, napi_callback_info cbinfo); @@ -24,6 +27,9 @@ class FunctionPointer { }; id registerBlock(napi_env env, Closure* closure, napi_value callback); +napi_value getCachedBlockCallback(napi_env env, void* blockPtr); +bool isObjCBlockObject(id obj); +const char* getObjCBlockSignature(void* blockPtr); NAPI_FUNCTION(registerBlock); diff --git a/NativeScript/ffi/Block.mm b/NativeScript/ffi/Block.mm index 09d7a5f8..f02989d5 100644 --- a/NativeScript/ffi/Block.mm +++ b/NativeScript/ffi/Block.mm @@ -1,12 +1,16 @@ #include "Block.h" #import +#include +#include +#include +#include +#include #include "Interop.h" #include "ObjCBridge.h" #include "js_native_api.h" #include "js_native_api_types.h" #include "node_api_util.h" #include "objc/runtime.h" -#include struct Block_descriptor_1 { unsigned long int reserved; // NULL @@ -33,11 +37,67 @@ constexpr int kBlockNeedsFree = (1 << 24); constexpr int kBlockHasCopyDispose = (1 << 25); constexpr int kBlockRefCountOne = (1 << 1); +constexpr int kBlockHasSignature = (1 << 30); + +struct BlockJsFunctionEntry { + napi_ref ref = nullptr; + napi_env env = nullptr; + std::thread::id jsThreadId; + CFRunLoopRef jsRunLoop = nullptr; +}; + +std::unordered_map g_blockToJsFunction; +std::mutex g_blockToJsFunctionMutex; + +inline bool removeCachedBlockJsFunctionEntry(void* blockPtr, BlockJsFunctionEntry* entry) { + std::lock_guard lock(g_blockToJsFunctionMutex); + auto it = g_blockToJsFunction.find(blockPtr); + if (it == g_blockToJsFunction.end()) { + return false; + } + if (entry != nullptr) { + *entry = it->second; + } + g_blockToJsFunction.erase(it); + return true; +} + +inline void deleteBlockReferenceOnOwningLoop(const BlockJsFunctionEntry& entry) { + if (entry.ref == nullptr || entry.env == nullptr) { + return; + } + + if (entry.jsThreadId == std::this_thread::get_id()) { + napi_delete_reference(entry.env, entry.ref); + return; + } + + CFRunLoopRef runLoop = entry.jsRunLoop; + if (runLoop == nullptr) { + runLoop = CFRunLoopGetMain(); + } + + if (runLoop == nullptr) { + return; + } + + CFRetain(runLoop); + napi_env env = entry.env; + napi_ref ref = entry.ref; + CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{ + napi_delete_reference(env, ref); + CFRelease(runLoop); + }); + CFRunLoopWakeUp(runLoop); +} void block_copy(void* dest, void* src) { auto dst = static_cast(dest); auto source = static_cast(src); dst->closure = source->closure; + if (dst->closure != nullptr) { + dst->closure->retain(); + } } void block_release(void* src) { @@ -46,10 +106,15 @@ void block_release(void* src) { return; } + BlockJsFunctionEntry entry; + if (removeCachedBlockJsFunctionEntry(block, &entry)) { + deleteBlockReferenceOnOwningLoop(entry); + } + if (block->closure != nullptr) { - delete block->closure; - block->closure = nullptr; + block->closure->release(); } + block->closure = nullptr; } Block_descriptor_1 kBlockDescriptor = { @@ -57,21 +122,73 @@ void block_release(void* src) { .size = sizeof(Block_literal_1), .copy_helper = block_copy, .dispose_helper = block_release, - .signature = nullptr, + .signature = "v@?", }; +inline napi_value getCachedBlockJsFunction(napi_env env, void* blockPtr) { + BlockJsFunctionEntry removedEntry; + bool shouldDelete = false; + napi_value value = nullptr; + + { + std::lock_guard lock(g_blockToJsFunctionMutex); + auto it = g_blockToJsFunction.find(blockPtr); + if (it == g_blockToJsFunction.end()) { + return nullptr; + } + + value = nativescript::get_ref_value(env, it->second.ref); + if (value == nullptr) { + removedEntry = it->second; + g_blockToJsFunction.erase(it); + shouldDelete = true; + } + } + + if (shouldDelete) { + deleteBlockReferenceOnOwningLoop(removedEntry); + } + + return value; +} + +inline void cacheBlockJsFunction(napi_env env, void* blockPtr, napi_value jsFunction, + nativescript::Closure* closure) { + if (blockPtr == nullptr || jsFunction == nullptr) { + return; + } + std::lock_guard lock(g_blockToJsFunctionMutex); + if (g_blockToJsFunction.find(blockPtr) != g_blockToJsFunction.end()) { + return; + } + // Keep this weak so callback identity can round-trip without preventing GC. + BlockJsFunctionEntry entry; + entry.ref = nativescript::make_ref(env, jsFunction, 0); + entry.env = env; + entry.jsThreadId = closure != nullptr ? closure->jsThreadId : std::this_thread::get_id(); + entry.jsRunLoop = closure != nullptr ? closure->jsRunLoop : CFRunLoopGetCurrent(); + g_blockToJsFunction[blockPtr] = entry; +} + } // namespace void block_finalize(napi_env env, void* data, void* hint) { + (void)env; + (void)hint; auto block = static_cast(data); if (block == nullptr) { return; } + BlockJsFunctionEntry entry; + if (removeCachedBlockJsFunctionEntry(block, &entry)) { + deleteBlockReferenceOnOwningLoop(entry); + } + if (block->closure != nullptr) { - delete block->closure; - block->closure = nullptr; + block->closure->release(); } + block->closure = nullptr; free(block); } @@ -94,7 +211,7 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) { } block->isa = mallocBlockISA != nullptr ? mallocBlockISA : stackBlockISA; - block->flags = kBlockNeedsFree | kBlockHasCopyDispose | kBlockRefCountOne; + block->flags = kBlockNeedsFree | kBlockHasCopyDispose | kBlockRefCountOne | kBlockHasSignature; block->reserved = 0; block->invoke = closure->fnptr; block->descriptor = &kBlockDescriptor; @@ -102,6 +219,12 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) { closure->func = make_ref(env, callback, 1); + // Expose the native block pointer on the JS callback so interop.handleof/sizeof + // can resolve pointers for blocks that round-trip through Objective-C. + napi_value ptrExternal; + napi_create_external(env, block, nullptr, nullptr, &ptrExternal); + napi_set_named_property(env, callback, "__ns_native_ptr", ptrExternal); + auto bridgeState = ObjCBridgeState::InstanceData(env); #ifndef ENABLE_JS_RUNTIME @@ -114,9 +237,59 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) { } #endif // ENABLE_JS_RUNTIME + cacheBlockJsFunction(env, block, callback, closure); + return (id)block; } +napi_value getCachedBlockCallback(napi_env env, void* blockPtr) { + return getCachedBlockJsFunction(env, blockPtr); +} + +bool isObjCBlockObject(id obj) { + if (obj == nil) { + return false; + } + + Class cls = object_getClass(obj); + if (cls == nil) { + return false; + } + + const char* className = class_getName(cls); + if (className == nullptr) { + return false; + } + + // Runtime block classes are typically internal names like + // __NSGlobalBlock__, __NSMallocBlock__, __NSStackBlock__. + return className[0] == '_' && className[1] == '_' && strstr(className, "Block") != nullptr; +} + +const char* getObjCBlockSignature(void* blockPtr) { + auto block = static_cast(blockPtr); + if (block == nullptr || block->descriptor == nullptr) { + return nullptr; + } + + if ((block->flags & kBlockHasSignature) == 0) { + return nullptr; + } + + // Descriptor layout: + // unsigned long reserved; + // unsigned long size; + // [copy_helper, dispose_helper] if BLOCK_HAS_COPY_DISPOSE + // const char* signature if BLOCK_HAS_SIGNATURE + auto descriptorCursor = reinterpret_cast(block->descriptor); + descriptorCursor += sizeof(unsigned long) * 2; + if ((block->flags & kBlockHasCopyDispose) != 0) { + descriptorCursor += sizeof(void*) * 2; + } + + return *reinterpret_cast(descriptorCursor); +} + NAPI_FUNCTION(registerBlock) { NAPI_CALLBACK_BEGIN(2) @@ -137,6 +310,13 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) { napi_value FunctionPointer::wrap(napi_env env, void* function, metagen::MDSectionOffset offset, bool isBlock) { + if (isBlock) { + napi_value cached = getCachedBlockJsFunction(env, function); + if (cached != nullptr) { + return cached; + } + } + FunctionPointer* ref = new FunctionPointer(); ref->function = function; ref->offset = offset; @@ -153,6 +333,77 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) { napi_create_function(env, isBlock ? "objcBlockWrapper" : "cFunctionWrapper", NAPI_AUTO_LENGTH, isBlock ? jsCallAsBlock : jsCallAsCFunction, ref, &result); + // Allow fast pointer extraction when JS function wrappers are passed back to native. + napi_ref nativePointerRef; + napi_wrap(env, result, function, nullptr, nullptr, &nativePointerRef); + (void)nativePointerRef; + + // Keep raw pointer metadata without overriding the function callback data. + // Overriding callback data breaks JS invocation for wrapped function pointers. + napi_value ptrExternal; + napi_create_external(env, function, nullptr, nullptr, &ptrExternal); + napi_property_descriptor ptrProp = { + .utf8name = "__ns_native_ptr", + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = ptrExternal, + .attributes = napi_default, + .data = nullptr, + }; + napi_define_properties(env, result, 1, &ptrProp); + + napi_ref jsRef; + napi_add_finalizer(env, result, ref, FunctionPointer::finalize, nullptr, &jsRef); + + return result; +} + +napi_value FunctionPointer::wrapWithEncoding(napi_env env, void* function, const char* encoding, + bool isBlock) { + if (function == nullptr || encoding == nullptr || encoding[0] == '\0') { + napi_value nullValue; + napi_get_null(env, &nullValue); + return nullValue; + } + + if (isBlock) { + napi_value cached = getCachedBlockJsFunction(env, function); + if (cached != nullptr) { + return cached; + } + } + + FunctionPointer* ref = new FunctionPointer(); + ref->function = function; + ref->offset = 0; + ref->ownsCif = true; + ref->cif = new Cif(env, encoding, isBlock ? 1 : 0); + + napi_value result; + napi_create_function(env, isBlock ? "objcBlockWrapper" : "cFunctionWrapper", NAPI_AUTO_LENGTH, + isBlock ? jsCallAsBlock : jsCallAsCFunction, ref, &result); + + // Allow fast pointer extraction when JS function wrappers are passed back to native. + napi_ref nativePointerRef; + napi_wrap(env, result, function, nullptr, nullptr, &nativePointerRef); + (void)nativePointerRef; + + // Keep raw pointer metadata without overriding the function callback data. + // Overriding callback data breaks JS invocation for wrapped function pointers. + napi_value ptrExternal; + napi_create_external(env, function, nullptr, nullptr, &ptrExternal); + napi_property_descriptor ptrProp = { + .utf8name = "__ns_native_ptr", + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = ptrExternal, + .attributes = napi_default, + .data = nullptr, + }; + napi_define_properties(env, result, 1, &ptrProp); + napi_ref jsRef; napi_add_finalizer(env, result, ref, FunctionPointer::finalize, nullptr, &jsRef); @@ -161,6 +412,10 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) { void FunctionPointer::finalize(napi_env env, void* finalize_data, void* finalize_hint) { auto ref = (FunctionPointer*)finalize_data; + if (ref->ownsCif && ref->cif != nullptr) { + delete ref->cif; + ref->cif = nullptr; + } delete ref; } diff --git a/NativeScript/ffi/CFunction.h b/NativeScript/ffi/CFunction.h index 3c31093c..d0003f12 100644 --- a/NativeScript/ffi/CFunction.h +++ b/NativeScript/ffi/CFunction.h @@ -1,6 +1,7 @@ #ifndef C_FUNCTION_H #define C_FUNCTION_H +#include #include "Cif.h" namespace nativescript { @@ -14,6 +15,12 @@ class CFunction { void* fnptr; Cif* cif = nullptr; + uint8_t dispatchFlags = 0; + bool dispatchLookupCached = false; + uint64_t dispatchLookupSignatureHash = 0; + uint64_t dispatchId = 0; + void* preparedInvoker = nullptr; + void* napiInvoker = nullptr; }; } // namespace nativescript diff --git a/NativeScript/ffi/CFunction.mm b/NativeScript/ffi/CFunction.mm index 3d7b29fa..296f4000 100644 --- a/NativeScript/ffi/CFunction.mm +++ b/NativeScript/ffi/CFunction.mm @@ -1,6 +1,12 @@ #include "CFunction.h" +#include +#include +#include +#include "Block.h" #include "ClassMember.h" +#include "Interop.h" #include "ObjCBridge.h" +#include "SignatureDispatch.h" #include "ffi/NativeScriptException.h" #include "ffi/Tasks.h" #ifdef ENABLE_JS_RUNTIME @@ -9,14 +15,207 @@ namespace nativescript { +namespace { + +inline bool unwrapCompatNativeHandleForCFunction(napi_env env, napi_value value, void** out) { + if (value == nullptr || out == nullptr) { + return false; + } + + if (Pointer::isInstance(env, value)) { + Pointer* ptr = Pointer::unwrap(env, value); + *out = ptr != nullptr ? ptr->data : nullptr; + return ptr != nullptr; + } + + if (Reference::isInstance(env, value)) { + Reference* ref = Reference::unwrap(env, value); + *out = ref != nullptr ? ref->data : nullptr; + return ref != nullptr; + } + + napi_valuetype valueType = napi_undefined; + if (napi_typeof(env, value, &valueType) != napi_ok) { + return false; + } + + if (valueType == napi_bigint) { + uint64_t raw = 0; + bool lossless = false; + if (napi_get_value_bigint_uint64(env, value, &raw, &lossless) != napi_ok) { + return false; + } + *out = reinterpret_cast(static_cast(raw)); + return true; + } + + if (valueType == napi_external) { + return napi_get_value_external(env, value, out) == napi_ok; + } + + if (valueType != napi_object && valueType != napi_function) { + return false; + } + + bool hasNativePointer = false; + if (napi_has_named_property(env, value, "__ns_native_ptr", &hasNativePointer) == napi_ok && + hasNativePointer) { + napi_value nativePointerValue = nullptr; + if (napi_get_named_property(env, value, "__ns_native_ptr", &nativePointerValue) == napi_ok && + napi_get_value_external(env, nativePointerValue, out) == napi_ok && *out != nullptr) { + return true; + } + } + + return napi_unwrap(env, value, out) == napi_ok && *out != nullptr; +} + +inline napi_value createCompatDispatchQueueWrapperForCFunction(napi_env env, + dispatch_queue_t queue) { + if (queue == nullptr) { + napi_value nullValue = nullptr; + napi_get_null(env, &nullValue); + return nullValue; + } + + return Pointer::create(env, reinterpret_cast(queue)); +} + +inline napi_value tryCallCompatLibdispatchFunction(napi_env env, napi_callback_info cbinfo, + const char* functionName) { + if (strcmp(functionName, "dispatch_get_global_queue") == 0) { + size_t argc = 2; + napi_value argv[2] = {nullptr, nullptr}; + napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, nullptr); + + int64_t identifier = 0; + if (argc > 0) { + napi_valuetype identifierType = napi_undefined; + if (napi_typeof(env, argv[0], &identifierType) == napi_ok && identifierType == napi_bigint) { + bool lossless = false; + if (napi_get_value_bigint_int64(env, argv[0], &identifier, &lossless) != napi_ok) { + napi_throw_type_error(env, nullptr, + "dispatch_get_global_queue expects a numeric identifier."); + return nullptr; + } + } else { + napi_value coercedIdentifier = nullptr; + if (napi_coerce_to_number(env, argv[0], &coercedIdentifier) != napi_ok || + napi_get_value_int64(env, coercedIdentifier, &identifier) != napi_ok) { + napi_throw_type_error(env, nullptr, + "dispatch_get_global_queue expects a numeric identifier."); + return nullptr; + } + } + } + + uint64_t flags = 0; + if (argc > 1) { + napi_valuetype flagsType = napi_undefined; + if (napi_typeof(env, argv[1], &flagsType) == napi_ok && flagsType == napi_bigint) { + bool lossless = false; + if (napi_get_value_bigint_uint64(env, argv[1], &flags, &lossless) != napi_ok) { + napi_throw_type_error(env, nullptr, "dispatch_get_global_queue expects numeric flags."); + return nullptr; + } + } else { + napi_value coercedFlags = nullptr; + int64_t signedFlags = 0; + if (napi_coerce_to_number(env, argv[1], &coercedFlags) != napi_ok || + napi_get_value_int64(env, coercedFlags, &signedFlags) != napi_ok) { + napi_throw_type_error(env, nullptr, "dispatch_get_global_queue expects numeric flags."); + return nullptr; + } + flags = static_cast(signedFlags); + } + } + + return createCompatDispatchQueueWrapperForCFunction( + env, dispatch_get_global_queue(identifier, flags)); + } + + if (strcmp(functionName, "dispatch_get_current_queue") == 0) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return createCompatDispatchQueueWrapperForCFunction(env, dispatch_get_current_queue()); +#pragma clang diagnostic pop + } + + if (strcmp(functionName, "dispatch_async") == 0) { + size_t argc = 2; + napi_value argv[2] = {nullptr, nullptr}; + napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, nullptr); + + if (argc < 2) { + napi_throw_type_error(env, nullptr, "dispatch_async expects a queue and callback."); + return nullptr; + } + + void* queueHandle = nullptr; + if (!unwrapCompatNativeHandleForCFunction(env, argv[0], &queueHandle) || + queueHandle == nullptr) { + napi_throw_type_error(env, nullptr, "dispatch_async expects a native queue handle."); + return nullptr; + } + + napi_valuetype callbackType = napi_undefined; + if (napi_typeof(env, argv[1], &callbackType) != napi_ok || callbackType != napi_function) { + napi_throw_type_error(env, nullptr, "dispatch_async expects a function callback."); + return nullptr; + } + + auto closure = new Closure(std::string("v"), true); + closure->env = env; + id block = registerBlock(env, closure, argv[1]); + dispatch_block_t dispatchBlock = (dispatch_block_t)block; + + dispatch_async(reinterpret_cast(queueHandle), dispatchBlock); + [block release]; + + napi_value undefinedValue = nullptr; + napi_get_undefined(env, &undefinedValue); + return undefinedValue; + } + + return nullptr; +} + +} // namespace + +inline void ensureCFunctionDispatchLookup(CFunction* function, Cif* cif) { + if (function == nullptr || cif == nullptr || cif->signatureHash == 0) { + if (function != nullptr) { + function->dispatchLookupCached = true; + function->dispatchLookupSignatureHash = 0; + function->dispatchId = 0; + function->preparedInvoker = nullptr; + function->napiInvoker = nullptr; + } + return; + } + + if (function->dispatchLookupCached && + function->dispatchLookupSignatureHash == cif->signatureHash) { + return; + } + + function->dispatchLookupSignatureHash = cif->signatureHash; + function->dispatchId = composeSignatureDispatchId( + cif->signatureHash, SignatureCallKind::CFunction, function->dispatchFlags); + function->preparedInvoker = + reinterpret_cast(lookupCFunctionPreparedInvoker(function->dispatchId)); + function->napiInvoker = reinterpret_cast(lookupCFunctionNapiInvoker(function->dispatchId)); + function->dispatchLookupCached = true; +} + void ObjCBridgeState::registerFunctionGlobals(napi_env env, napi_value global) { MDSectionOffset offset = metadata->functionsOffset; while (offset < metadata->protocolsOffset) { MDSectionOffset originalOffset = offset; auto name = metadata->getString(offset); offset += sizeof(MDSectionOffset); - auto signature = metadata->getOffset(offset); offset += sizeof(MDSectionOffset); + offset += sizeof(MDFunctionFlag); napi_property_descriptor prop = { .utf8name = name, @@ -40,9 +239,11 @@ auto sigOffset = metadata->signaturesOffset + metadata->getOffset(offset + sizeof(MDSectionOffset)); + MDFunctionFlag functionFlags = metadata->getFunctionFlag(offset + sizeof(MDSectionOffset) * 2); auto cFunction = new CFunction(dlsym(self_dl, metadata->getString(offset))); cFunction->cif = getCFunctionCif(env, sigOffset); + cFunction->dispatchFlags = (functionFlags & mdFunctionReturnOwned) != 0 ? 1 : 0; cFunctionCache[offset] = cFunction; return cFunction; @@ -58,12 +259,75 @@ auto name = bridgeState->metadata->getString(offset); + if (strcmp(name, "dispatch_async") == 0 || strcmp(name, "dispatch_get_current_queue") == 0 || + strcmp(name, "dispatch_get_global_queue") == 0) { + return tryCallCompatLibdispatchFunction(env, cbinfo, name); + } + auto func = bridgeState->getCFunction(env, offset); auto cif = func->cif; + ensureCFunctionDispatchLookup(func, cif); + auto preparedInvoker = reinterpret_cast(func->preparedInvoker); + auto napiInvoker = reinterpret_cast(func->napiInvoker); + + MDFunctionFlag functionFlags = + bridgeState->metadata->getFunctionFlag(offset + sizeof(MDSectionOffset) * 2); - size_t argc = cif->argc; - napi_get_cb_info(env, cbinfo, &argc, cif->argv, nullptr, nullptr); + const napi_value* invocationArgs = nullptr; + std::vector dynamicArgs; + std::vector paddedArgs; + napi_value stackArgs[16]; + if (cif->argc > 0) { + size_t actualArgc = 16; + napi_get_cb_info(env, cbinfo, &actualArgc, stackArgs, nullptr, nullptr); + + const napi_value* callArgs = stackArgs; + if (actualArgc > 16) { + dynamicArgs.resize(actualArgc); + size_t retryArgc = actualArgc; + napi_get_cb_info(env, cbinfo, &retryArgc, dynamicArgs.data(), nullptr, nullptr); + dynamicArgs.resize(retryArgc); + actualArgc = retryArgc; + callArgs = dynamicArgs.data(); + } + + invocationArgs = callArgs; + if (actualArgc != cif->argc) { + napi_value jsUndefined = nullptr; + napi_get_undefined(env, &jsUndefined); + paddedArgs.assign(cif->argc, jsUndefined); + size_t copyArgc = actualArgc < cif->argc ? actualArgc : cif->argc; + if (copyArgc > 0) { + memcpy(paddedArgs.data(), callArgs, copyArgc * sizeof(napi_value)); + } + invocationArgs = paddedArgs.data(); + } + } + + uint32_t toJSFlags = kCStringAsReference; + if ((functionFlags & mdFunctionReturnOwned) != 0) { + toJSFlags |= kReturnOwned; + } + + const bool isMainEntrypoint = + strcmp(name, "UIApplicationMain") == 0 || strcmp(name, "NSApplicationMain") == 0; + + if (napiInvoker != nullptr && !cif->skipGeneratedNapiDispatch && !isMainEntrypoint) { + @try { + if (!napiInvoker(env, cif, func->fnptr, invocationArgs, cif->rvalue)) { + return nullptr; + } + } @catch (NSException* exception) { + std::string message = exception.description.UTF8String; + NSLog(@"ObjC->JS: Exception in CFunction (direct): %s", message.c_str()); + nativescript::NativeScriptException nativeScriptException(message); + nativeScriptException.ReThrowToJS(env); + return nullptr; + } + + return cif->returnType->toJS(env, cif->rvalue, toJSFlags); + } void* avalues[cif->argc]; void* rvalue = cif->rvalue; @@ -75,22 +339,27 @@ for (unsigned int i = 0; i < cif->argc; i++) { shouldFree[i] = false; avalues[i] = cif->avalues[i]; - cif->argTypes[i]->toNative(env, cif->argv[i], avalues[i], &shouldFree[i], &shouldFreeAny); + cif->argTypes[i]->toNative(env, invocationArgs[i], avalues[i], &shouldFree[i], + &shouldFreeAny); } } #ifdef ENABLE_JS_RUNTIME - if (strcmp(name, "UIApplicationMain") == 0 || strcmp(name, "NSApplicationMain") == 0) { - void **avaluesPtr = new void*[cif->argc]; + if (isMainEntrypoint) { + void** avaluesPtr = new void*[cif->argc]; memcpy(avaluesPtr, avalues, cif->argc * sizeof(void*)); - Tasks::Register([env, cif, func, rvalue, avaluesPtr]() { - void * avalues[cif->argc]; + Tasks::Register([env, cif, func, preparedInvoker, rvalue, avaluesPtr]() { + void* avalues[cif->argc]; memcpy(avalues, avaluesPtr, cif->argc * sizeof(void*)); delete[] avaluesPtr; - + @try { - ffi_call(&cif->cif, FFI_FN(func->fnptr), rvalue, avalues); + if (preparedInvoker != nullptr) { + preparedInvoker(func->fnptr, avalues, rvalue); + } else { + ffi_call(&cif->cif, FFI_FN(func->fnptr), rvalue, avalues); + } } @catch (NSException* exception) { NapiScope scope(env); std::string message = exception.description.UTF8String; @@ -99,13 +368,17 @@ nativeScriptException.ReThrowToJS(env); } }); - + return nullptr; } #endif @try { - ffi_call(&cif->cif, FFI_FN(func->fnptr), rvalue, avalues); + if (preparedInvoker != nullptr) { + preparedInvoker(func->fnptr, avalues, rvalue); + } else { + ffi_call(&cif->cif, FFI_FN(func->fnptr), rvalue, avalues); + } } @catch (NSException* exception) { std::string message = exception.description.UTF8String; NSLog(@"ObjC->JS: Exception in CFunction: %s", message.c_str()); @@ -115,14 +388,26 @@ } if (shouldFreeAny) { + void* returnPointerValue = nullptr; + bool returnIsPointer = cif->returnType != nullptr && cif->returnType->type == &ffi_type_pointer; + if (returnIsPointer && rvalue != nullptr) { + returnPointerValue = *((void**)rvalue); + } + for (unsigned int i = 0; i < cif->argc; i++) { if (shouldFree[i]) { + if (returnPointerValue != nullptr && avalues[i] != nullptr) { + void* argPointerValue = *((void**)avalues[i]); + if (argPointerValue == returnPointerValue) { + continue; + } + } cif->argTypes[i]->free(env, *((void**)avalues[i])); } } } - return cif->returnType->toJS(env, rvalue); + return cif->returnType->toJS(env, rvalue, toJSFlags); } CFunction::~CFunction() { cif = nullptr; } diff --git a/NativeScript/ffi/Cif.h b/NativeScript/ffi/Cif.h index 574af6d6..ee483acf 100644 --- a/NativeScript/ffi/Cif.h +++ b/NativeScript/ffi/Cif.h @@ -1,6 +1,7 @@ #ifndef METHOD_CIF_H #define METHOD_CIF_H +#include #include #include "MetadataReader.h" @@ -18,6 +19,8 @@ class Cif { size_t frameLength; size_t rvalueLength; bool isVariadic = false; + uint64_t signatureHash = 0; + bool skipGeneratedNapiDispatch = false; void* rvalue; void** avalues; @@ -32,10 +35,11 @@ class Cif { bool shouldFreeAny; bool* shouldFree; - Cif(napi_env env, std::string typeEncoding); + Cif(napi_env env, std::string typeEncoding, unsigned int implicitArgc = 2); + Cif(napi_env env, Method method); Cif(napi_env env, MDMetadataReader* reader, MDSectionOffset offset, bool isMethod = false, bool isBlock = false); - + ~Cif(); }; diff --git a/NativeScript/ffi/Cif.mm b/NativeScript/ffi/Cif.mm index 3c4ea5fa..41c9e9d9 100644 --- a/NativeScript/ffi/Cif.mm +++ b/NativeScript/ffi/Cif.mm @@ -1,15 +1,223 @@ #include "Cif.h" #include +#include +#include #include +#include +#include #include #include "Metadata.h" #include "MetadataReader.h" #include "ObjCBridge.h" #include "TypeConv.h" #include "Util.h" -#include namespace nativescript { +namespace { + +constexpr uint64_t kFNV64OffsetBasis = 14695981039346656037ull; +constexpr uint64_t kFNV64Prime = 1099511628211ull; + +uint64_t hashBytesFnv1a(const void* data, size_t size, uint64_t seed = kFNV64OffsetBasis) { + const auto* bytes = static_cast(data); + uint64_t hash = seed; + for (size_t i = 0; i < size; i++) { + hash ^= static_cast(bytes[i]); + hash *= kFNV64Prime; + } + return hash; +} + +MDTypeKind canonicalizeSignatureTypeKind(MDTypeKind kind) { + switch (kind) { + case mdTypeAnyObject: + case mdTypeProtocolObject: + case mdTypeClassObject: + case mdTypeInstanceObject: + case mdTypeNSStringObject: + case mdTypeNSMutableStringObject: + return mdTypeAnyObject; + default: + return kind; + } +} + +template +void appendIntegralToHash(uint64_t* hash, T value) { + using Unsigned = typename std::make_unsigned::type; + Unsigned unsignedValue = static_cast(value); + for (size_t i = 0; i < sizeof(Unsigned); i++) { + const uint8_t byte = static_cast((unsignedValue >> (i * 8)) & 0xFF); + *hash = hashBytesFnv1a(&byte, sizeof(byte), *hash); + } +} + +bool appendMetadataSignatureHash(MDMetadataReader* reader, MDSectionOffset signatureOffset, + std::unordered_set* activeSignatures, + uint64_t* hash); + +inline bool typeRequiresSlowGeneratedNapiDispatch(const std::shared_ptr& type) { + if (type == nullptr) { + return false; + } + + switch (type->kind) { + case mdTypeUChar: + case mdTypeUInt8: + return true; + default: + return false; + } +} + +inline void updateGeneratedNapiDispatchCompatibility(Cif* cif) { + if (cif == nullptr) { + return; + } + + cif->skipGeneratedNapiDispatch = typeRequiresSlowGeneratedNapiDispatch(cif->returnType); + if (cif->skipGeneratedNapiDispatch) { + return; + } + + for (const auto& argType : cif->argTypes) { + if (typeRequiresSlowGeneratedNapiDispatch(argType)) { + cif->skipGeneratedNapiDispatch = true; + return; + } + } +} + +bool appendMetadataTypeHash(MDMetadataReader* reader, MDSectionOffset* offset, + std::unordered_set* activeSignatures, uint64_t* hash) { + if (reader == nullptr || offset == nullptr || hash == nullptr || activeSignatures == nullptr) { + return false; + } + + const MDTypeKind kindWithFlags = reader->getTypeKind(*offset); + *offset += sizeof(MDTypeKind); + const MDTypeKind rawKind = + static_cast((kindWithFlags & ~mdTypeFlagNext) & ~mdTypeFlagVariadic); + + appendIntegralToHash(hash, 0xB0); + const MDTypeKind canonicalKind = canonicalizeSignatureTypeKind(rawKind); + appendIntegralToHash(hash, static_cast(canonicalKind)); + + switch (rawKind) { + case mdTypeArray: + case mdTypeVector: + case mdTypeExtVector: + case mdTypeComplex: { + const auto arraySize = reader->getArraySize(*offset); + *offset += sizeof(uint16_t); + appendIntegralToHash(hash, arraySize); + if (!appendMetadataTypeHash(reader, offset, activeSignatures, hash)) { + return false; + } + break; + } + + case mdTypeStruct: { + const auto structOffset = reader->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + appendIntegralToHash(hash, structOffset); + break; + } + + case mdTypeClassObject: { + auto classOffset = reader->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + bool hasNext = (classOffset & mdSectionOffsetNext) != 0; + while (hasNext) { + auto protocolOffset = reader->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + hasNext = (protocolOffset & mdSectionOffsetNext) != 0; + } + break; + } + + case mdTypeProtocolObject: { + bool hasNext = true; + while (hasNext) { + auto protocolOffset = reader->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + hasNext = (protocolOffset & mdSectionOffsetNext) != 0; + } + break; + } + + case mdTypePointer: + if (!appendMetadataTypeHash(reader, offset, activeSignatures, hash)) { + return false; + } + break; + + case mdTypeBlock: + case mdTypeFunctionPointer: { + const auto nestedSignatureOffset = reader->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + if (nestedSignatureOffset != MD_SECTION_OFFSET_NULL) { + const auto nestedAbsoluteOffset = reader->signaturesOffset + nestedSignatureOffset; + if (!appendMetadataSignatureHash(reader, nestedAbsoluteOffset, activeSignatures, hash)) { + return false; + } + } + break; + } + + default: + break; + } + + appendIntegralToHash(hash, 0xBF); + return true; +} + +bool appendMetadataSignatureHash(MDMetadataReader* reader, MDSectionOffset signatureOffset, + std::unordered_set* activeSignatures, + uint64_t* hash) { + if (reader == nullptr || hash == nullptr || activeSignatures == nullptr) { + return false; + } + + if (activeSignatures->find(signatureOffset) != activeSignatures->end()) { + appendIntegralToHash(hash, 0xEE); + return true; + } + activeSignatures->insert(signatureOffset); + + MDSectionOffset offset = signatureOffset; + const MDTypeKind returnTypeKind = reader->getTypeKind(offset); + bool next = (returnTypeKind & mdTypeFlagNext) != 0; + const bool isVariadic = (returnTypeKind & mdTypeFlagVariadic) != 0; + + appendIntegralToHash(hash, 0xA0); + appendIntegralToHash(hash, isVariadic ? 1 : 0); + + if (!appendMetadataTypeHash(reader, &offset, activeSignatures, hash)) { + activeSignatures->erase(signatureOffset); + return false; + } + + uint32_t argCount = 0; + while (next) { + const MDTypeKind argTypeKind = reader->getTypeKind(offset); + next = (argTypeKind & mdTypeFlagNext) != 0; + if (!appendMetadataTypeHash(reader, &offset, activeSignatures, hash)) { + activeSignatures->erase(signatureOffset); + return false; + } + argCount++; + } + + appendIntegralToHash(hash, argCount); + appendIntegralToHash(hash, 0xAF); + + activeSignatures->erase(signatureOffset); + return true; +} + +} // namespace // Essentially, we cache libffi structures per unique method signature, // this helps us avoid the overhead of creating them on the fly for each @@ -21,7 +229,7 @@ return find; } - auto cif = new Cif(env, encoding); + auto cif = new Cif(env, method); this->cifs[encoding] = cif; return cif; @@ -63,10 +271,11 @@ return cif; } -Cif::Cif(napi_env env, std::string encoding) { +Cif::Cif(napi_env env, std::string encoding, unsigned int implicitArgc) { auto signature = [NSMethodSignature signatureWithObjCTypes:encoding.c_str()]; unsigned long numberOfArguments = signature.numberOfArguments; - this->argc = (int)numberOfArguments - 2; + unsigned long skippedArgs = std::min(numberOfArguments, implicitArgc); + this->argc = (int)(numberOfArguments - skippedArgs); this->argv = (napi_value*)malloc(sizeof(napi_value) * this->argc); unsigned int totalArgc = (unsigned int)numberOfArguments; @@ -84,27 +293,27 @@ this->rvalueLength = methodReturnLength; this->frameLength = frameLength; - this->avalues = (void**)malloc(sizeof(void*) * totalArgc); - memset(this->avalues, 0, sizeof(void*) * totalArgc); + this->avalues = this->argc > 0 ? (void**)malloc(sizeof(void*) * this->argc) : nullptr; + if (this->avalues != nullptr) { + memset(this->avalues, 0, sizeof(void*) * this->argc); + } this->shouldFree = (bool*)malloc(sizeof(bool) * this->argc); memset(this->shouldFree, false, sizeof(bool) * this->argc); this->shouldFreeAny = false; - this->avaluesAllocStart = 2; + this->avaluesAllocStart = 0; this->avaluesAllocCount = 0; for (int i = 0; i < numberOfArguments; i++) { const char* argenc = [signature getArgumentTypeAtIndex:i]; auto argTypeInfo = TypeConv::Make(env, &argenc); - this->atypes[i] = argTypeInfo->type; + this->atypes[i] = argTypeInfo->ffiTypeForArgument(); - if (i >= 2) { + if (i >= skippedArgs) { this->argTypes.push_back(argTypeInfo); } } - [signature release]; - ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, totalArgc, rtype, this->atypes); if (status != FFI_OK) { @@ -112,14 +321,77 @@ return; } - for (unsigned int i = 2; i < totalArgc; i++) { - this->avalues[i] = malloc(cif.arg_types[i]->size); + for (unsigned int i = 0; i < this->argc; i++) { + this->avalues[i] = malloc(cif.arg_types[i + skippedArgs]->size); this->avaluesAllocCount++; } + + updateGeneratedNapiDispatchCompatibility(this); +} + +Cif::Cif(napi_env env, Method method) { + const unsigned int totalArgc = method_getNumberOfArguments(method); + this->argc = totalArgc >= 2 ? totalArgc - 2 : 0; + this->argv = this->argc > 0 ? (napi_value*)malloc(sizeof(napi_value) * this->argc) : nullptr; + + char* returnTypeEnc = method_copyReturnType(method); + const char* returnTypePtr = returnTypeEnc; + this->returnType = TypeConv::Make(env, &returnTypePtr); + if (returnTypeEnc != nullptr) { + free(returnTypeEnc); + } + + ffi_type* rtype = this->returnType->type; + this->atypes = (ffi_type**)malloc(sizeof(ffi_type*) * totalArgc); + + this->rvalueLength = std::max(1, rtype->size); + this->rvalue = malloc(this->rvalueLength); + this->frameLength = 0; + + this->avalues = this->argc > 0 ? (void**)malloc(sizeof(void*) * this->argc) : nullptr; + if (this->avalues != nullptr) { + memset(this->avalues, 0, sizeof(void*) * this->argc); + } + + this->shouldFree = this->argc > 0 ? (bool*)malloc(sizeof(bool) * this->argc) : nullptr; + if (this->shouldFree != nullptr) { + memset(this->shouldFree, false, sizeof(bool) * this->argc); + } + this->shouldFreeAny = false; + this->avaluesAllocStart = 0; + this->avaluesAllocCount = 0; + + for (unsigned int i = 0; i < totalArgc; i++) { + char* argEnc = method_copyArgumentType(method, i); + const char* argEncPtr = argEnc; + auto argTypeInfo = TypeConv::Make(env, &argEncPtr); + if (argEnc != nullptr) { + free(argEnc); + } + + this->atypes[i] = argTypeInfo->ffiTypeForArgument(); + if (i >= 2) { + this->argTypes.push_back(argTypeInfo); + } + } + + ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, totalArgc, rtype, this->atypes); + if (status != FFI_OK) { + std::cout << "Failed to prepare CIF, libffi returned error:" << status << std::endl; + return; + } + + for (unsigned int i = 0; i < this->argc; i++) { + this->avalues[i] = malloc(cif.arg_types[i + 2]->size); + this->avaluesAllocCount++; + } + + updateGeneratedNapiDispatchCompatibility(this); } Cif::Cif(napi_env env, MDMetadataReader* reader, MDSectionOffset offset, bool isMethod, bool isBlock) { + MDSectionOffset signatureStart = offset; auto returnTypeKind = reader->getTypeKind(offset); bool next = ((MDTypeFlag)returnTypeKind & mdTypeFlagNext) != 0; isVariadic = ((MDTypeFlag)returnTypeKind & mdTypeFlagVariadic) != 0; @@ -164,7 +436,7 @@ } for (int i = 0; i < argc; i++) { - atypes[i + implicitArgs] = argTypes[i]->type; + atypes[i + implicitArgs] = argTypes[i]->ffiTypeForArgument(); shouldFree[i] = false; } } else { @@ -189,6 +461,17 @@ rvalue = malloc(cif.rtype->size); rvalueLength = cif.rtype->size; + + if (signatureStart != MD_SECTION_OFFSET_NULL) { + uint64_t canonicalSignatureHash = kFNV64OffsetBasis; + std::unordered_set activeSignatures; + if (appendMetadataSignatureHash(reader, signatureStart, &activeSignatures, + &canonicalSignatureHash)) { + signatureHash = canonicalSignatureHash; + } + } + + updateGeneratedNapiDispatchCompatibility(this); } Cif::~Cif() { diff --git a/NativeScript/ffi/Class.mm b/NativeScript/ffi/Class.mm index 2c844eba..01579c0b 100644 --- a/NativeScript/ffi/Class.mm +++ b/NativeScript/ffi/Class.mm @@ -1,6 +1,7 @@ #include "Class.h" #include "ClassBuilder.h" #include "ClassMember.h" +#include "Interop.h" #include "Metadata.h" #include "MetadataReader.h" #include "ObjCBridge.h" @@ -11,10 +12,56 @@ #include "node_api_util.h" #import +#include #include namespace nativescript { +napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo); + +inline bool tryGetInteropPointerArg(napi_env env, napi_value value, void** out) { + if (out == nullptr || value == nullptr) { + return false; + } + + *out = nullptr; + + napi_valuetype argType = napi_undefined; + napi_typeof(env, value, &argType); + + if (argType == napi_external) { + return napi_get_value_external(env, value, out) == napi_ok && *out != nullptr; + } + + if (argType == napi_bigint) { + uint64_t raw = 0; + bool lossless = false; + if (napi_get_value_bigint_uint64(env, value, &raw, &lossless) != napi_ok) { + return false; + } + *out = reinterpret_cast(raw); + return *out != nullptr; + } + + if (argType != napi_object) { + return false; + } + + if (Pointer::isInstance(env, value)) { + Pointer* ptr = Pointer::unwrap(env, value); + *out = ptr != nullptr ? ptr->data : nullptr; + return *out != nullptr; + } + + if (Reference::isInstance(env, value)) { + Reference* ref = Reference::unwrap(env, value); + *out = ref != nullptr ? ref->data : nullptr; + return *out != nullptr; + } + + return false; +} + void ObjCBridgeState::registerClassGlobals(napi_env env, napi_value global) { MDSectionOffset offset = metadata->classesOffset; while (offset < metadata->structsOffset) { @@ -194,11 +241,43 @@ void setupObjCClassDecorator(napi_env env) { return get_ref_value(env, cls->constructor); } +NAPI_FUNCTION(classSuperclassFallback) { + napi_value jsThis; + napi_get_cb_info(env, cbinfo, nullptr, nullptr, &jsThis, nullptr); + + Class currentClass = nil; + napi_unwrap(env, jsThis, (void**)¤tClass); + if (currentClass == nil) { + return nullptr; + } + + Class superClass = class_getSuperclass(currentClass); + if (superClass == nil) { + return nullptr; + } + + auto bridgeState = ObjCBridgeState::InstanceData(env); + auto find = bridgeState->classesByPointer.find(superClass); + if (find != bridgeState->classesByPointer.end()) { + return get_ref_value(env, find->second->constructor); + } + + auto mdClass = bridgeState->mdClassesByPointer.find(superClass); + if (mdClass != bridgeState->mdClassesByPointer.end()) { + auto bridgedClass = bridgeState->getClass(env, mdClass->second); + return get_ref_value(env, bridgedClass->constructor); + } + + return bridgeState->getObject(env, (id)superClass, kUnownedObject, 0, nullptr); +} + NAPI_FUNCTION(BridgedConstructor) { - NAPI_CALLBACK_BEGIN(1) + NAPI_CALLBACK_BEGIN(16) - napi_valuetype jsType; - napi_typeof(env, argv[0], &jsType); + napi_valuetype jsType = napi_undefined; + if (argc > 0) { + napi_typeof(env, argv[0], &jsType); + } id object = nil; @@ -220,6 +299,28 @@ void setupObjCClassDecorator(napi_env env) { if (jsType == napi_external) { return jsThis; } else { + // Backward compatibility: allow `new Class(pointer)` to wrap an existing + // native instance instead of running initializer resolution. + if (argc == 1 && argv[0] != nullptr) { + void* rawPointer = nullptr; + if (tryGetInteropPointerArg(env, argv[0], &rawPointer) && rawPointer != nullptr) { + if (napi_value cached = bridgeState->getCachedHandleObject(env, rawPointer); + cached != nullptr) { + return cached; + } + + object = (id)rawPointer; + if (napi_value existing = bridgeState->findCachedObjectWrapper(env, object); + existing != nullptr) { + return existing; + } + + jsThis = bridgeState->proxyNativeObject(env, jsThis, object); + napi_wrap(env, jsThis, object, nullptr, nullptr, nullptr); + return jsThis; + } + } + bool supercall = class_conformsToProtocol(cls, @protocol(ObjCBridgeClassBuilderProtocol)); if (supercall) { @@ -227,13 +328,28 @@ void setupObjCClassDecorator(napi_env env) { if (!builder->isFinal) builder->build(); } - object = [cls new]; + // Allocate first, then run initializer resolution through the bridged + // JS "init" method so constructor arguments participate in selector + // matching (including Swift-style token objects). + object = [cls alloc]; jsThis = bridgeState->proxyNativeObject(env, jsThis, object); } napi_wrap(env, jsThis, object, nullptr, nullptr, nullptr); - return jsThis; + napi_value initMethod; + napi_status initStatus = napi_get_named_property(env, jsThis, "init", &initMethod); + if (initStatus != napi_ok || initMethod == nullptr) { + return jsThis; + } + + napi_value initResult; + initStatus = napi_call_function(env, jsThis, initMethod, argc, argv, &initResult); + if (initStatus != napi_ok) { + return nullptr; + } + + return initResult != nullptr ? initResult : jsThis; } // Used to display the description of a native object in console.log. @@ -428,10 +544,10 @@ napi_value toJS(napi_env env) { // Every Bridged Class extends the NativeObject class. void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value constructor, - ObjCProtocol* protocol) { - ObjCClassMember::defineMembers(env, members, protocol->membersOffset, constructor); + ObjCProtocol* protocol, ObjCClass* cls) { + ObjCClassMember::defineMembers(env, members, protocol->membersOffset, constructor, cls); for (auto protocol : protocol->protocols) { - defineProtocolMembers(env, members, constructor, protocol); + defineProtocolMembers(env, members, constructor, protocol, cls); } } @@ -450,8 +566,10 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value MDSectionOffset superClassOffset = MD_SECTION_OFFSET_NULL; bool hasMembers = false; + std::string jsConstructorName; if (isNativeObject) { name = NativeObjectName; + jsConstructorName = name; nativeClass = nil; } else { auto nameOffset = bridgeState->metadata->getOffset(offset); @@ -465,6 +583,10 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value if (runtimeNameOffset != MD_SECTION_OFFSET_NULL) { runtimeName = bridgeState->metadata->resolveString(runtimeNameOffset); } + jsConstructorName = runtimeName; + if (jsConstructorName.empty()) { + jsConstructorName = name; + } nativeClass = objc_getClass(runtimeName); while (hasProtocols) { auto protocolOffset = bridgeState->metadata->getOffset(offset); @@ -481,8 +603,8 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value napi_value constructor, prototype; - napi_define_class(env, name.c_str(), name.length(), JS_BridgedConstructor, (void*)nativeClass, 0, - nil, &constructor); + napi_define_class(env, jsConstructorName.c_str(), jsConstructorName.length(), + JS_BridgedConstructor, (void*)nativeClass, 0, nil, &constructor); if (nativeClass != nil) { napi_wrap(env, constructor, (void*)nativeClass, nil, nil, nil); @@ -497,13 +619,6 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value // class extends - we need to find the super class and inherit from it, if it // exists. if (!isNativeObject) { - for (auto protocolOffset : protocolOffsets) { - auto protocol = - bridgeState->getProtocol(env, protocolOffset + bridgeState->metadata->protocolsOffset); - if (protocol == nil) continue; - defineProtocolMembers(env, members, constructor, protocol); - } - if (superClassOffset != MD_SECTION_OFFSET_NULL) { superClassOffset += bridgeState->metadata->classesOffset; } @@ -516,6 +631,13 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value superPrototype = get_ref_value(env, superclass->prototype); napi_inherits(env, constructor, superConstructor); } + + for (auto protocolOffset : protocolOffsets) { + auto protocol = + bridgeState->getProtocol(env, protocolOffset + bridgeState->metadata->protocolsOffset); + if (protocol == nil) continue; + defineProtocolMembers(env, members, constructor, protocol, this); + } } else { superclass = nullptr; } @@ -581,6 +703,25 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value napi_define_properties(env, constructor, 1, &properties[2]); + bool hasSuperclass = false; + napi_value superclassKey = nullptr; + napi_create_string_utf8(env, "superclass", NAPI_AUTO_LENGTH, &superclassKey); + napi_has_own_property(env, constructor, superclassKey, &hasSuperclass); + if (!hasSuperclass) { + napi_property_descriptor superclassProperty = { + .utf8name = "superclass", + .name = nil, + .method = JS_classSuperclassFallback, + .getter = nil, + .setter = nil, + .value = nil, + .attributes = + (napi_property_attributes)(napi_configurable | napi_writable | napi_enumerable), + .data = nil, + }; + napi_define_properties(env, constructor, 1, &superclassProperty); + } + if (type == napi_symbol) { properties[0].name = SymbolDispose; properties[0].method = JS_releaseObject; @@ -602,6 +743,73 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value if (!hasMembers) return; ObjCClassMember::defineMembers(env, members, offset, constructor, this); + + auto hasOwnNamedProperty = [&](napi_value object, const char* propertyName) { + napi_value key = nullptr; + napi_create_string_utf8(env, propertyName, NAPI_AUTO_LENGTH, &key); + bool hasOwn = false; + napi_has_own_property(env, object, key, &hasOwn); + return hasOwn; + }; + + if (!hasOwnNamedProperty(constructor, "alloc")) { + napi_property_descriptor allocProperty = { + .utf8name = "alloc", + .name = nil, + .method = JS_NSObject_alloc, + .getter = nil, + .setter = nil, + .value = nil, + .attributes = + (napi_property_attributes)(napi_configurable | napi_writable | napi_enumerable), + .data = nil, + }; + napi_define_properties(env, constructor, 1, &allocProperty); + } + + if (!hasOwnNamedProperty(constructor, "arguments") || + !hasOwnNamedProperty(constructor, "caller")) { + napi_value undefinedValue = nullptr; + napi_get_undefined(env, &undefinedValue); + napi_property_descriptor slots[] = { + { + .utf8name = "arguments", + .name = nil, + .method = nil, + .getter = nil, + .setter = nil, + .value = undefinedValue, + .attributes = (napi_property_attributes)(napi_configurable | napi_writable), + .data = nil, + }, + { + .utf8name = "caller", + .name = nil, + .method = nil, + .getter = nil, + .setter = nil, + .value = undefinedValue, + .attributes = (napi_property_attributes)(napi_configurable | napi_writable), + .data = nil, + }, + }; + napi_define_properties(env, constructor, 2, slots); + } + + if (!hasOwnNamedProperty(prototype, "toString")) { + napi_property_descriptor toStringProperty = { + .utf8name = "toString", + .name = nil, + .method = JS_CustomInspect, + .getter = nil, + .setter = nil, + .value = nil, + .attributes = + (napi_property_attributes)(napi_configurable | napi_writable | napi_enumerable), + .data = nil, + }; + napi_define_properties(env, prototype, 1, &toStringProperty); + } } ObjCClass::~ObjCClass() { diff --git a/NativeScript/ffi/ClassBuilder.h b/NativeScript/ffi/ClassBuilder.h index 65be3d82..ccd07fad 100644 --- a/NativeScript/ffi/ClassBuilder.h +++ b/NativeScript/ffi/ClassBuilder.h @@ -19,12 +19,13 @@ class ClassBuilder : public ObjCClass { ~ClassBuilder(); void addProtocol(ObjCProtocol* protocol); - MethodDescriptor* lookupMethodDescriptor(std::string& name); + std::vector lookupMethodDescriptors(std::string& name, bool setter = false); + MethodDescriptor* lookupMethodDescriptor(std::string& name, bool setter = false); void addMethod(std::string& name, MethodDescriptor* desc, napi_value key, napi_value func = nullptr); void build(); - + // Static callback for extending native classes static napi_value ExtendCallback(napi_env env, napi_callback_info info); diff --git a/NativeScript/ffi/ClassBuilder.mm b/NativeScript/ffi/ClassBuilder.mm index 5a29d911..ad471905 100644 --- a/NativeScript/ffi/ClassBuilder.mm +++ b/NativeScript/ffi/ClassBuilder.mm @@ -2,13 +2,390 @@ #import #include #include "Closure.h" +#include "Interop.h" #include "Metadata.h" #include "ObjCBridge.h" +#include "Protocol.h" #include "Util.h" #include "js_native_api.h" #include "node_api_util.h" +#include namespace nativescript { +namespace { +std::unordered_map gKnownExposedMethods; +std::mutex gClassBuilderEnvMutex; +std::unordered_map gClassBuilderEnvs; + +void registerClassBuilderEnv(Class nativeClass, napi_env env) { + if (nativeClass == Nil || env == nullptr) { + return; + } + + std::lock_guard lock(gClassBuilderEnvMutex); + gClassBuilderEnvs[nativeClass] = env; +} + +void unregisterClassBuilderEnv(Class nativeClass) { + if (nativeClass == Nil) { + return; + } + + std::lock_guard lock(gClassBuilderEnvMutex); + gClassBuilderEnvs.erase(nativeClass); +} + +napi_env resolveClassBuilderEnv(id self) { + if (self == nil) { + return nullptr; + } + + Class currentClass = object_getClass(self); + if (currentClass == Nil) { + return nullptr; + } + + std::lock_guard lock(gClassBuilderEnvMutex); + while (currentClass != Nil) { + auto find = gClassBuilderEnvs.find(currentClass); + if (find != gClassBuilderEnvs.end()) { + return find->second; + } + currentClass = class_getSuperclass(currentClass); + } + + return nullptr; +} + +const char* kInstallSuperAccessorSource = R"( + (function (prototype, basePrototype) { + const superCacheKey = Symbol.for("__ns_super_proxy__"); + Object.defineProperty(prototype, "super", { + configurable: true, + enumerable: false, + get: function () { + const self = this; + if (self == null) { + return undefined; + } + + const existing = self[superCacheKey]; + if (existing && existing.base === basePrototype) { + return existing.proxy; + } + + const proxy = new Proxy(basePrototype, { + get(target, key) { + let current = target; + while (current != null) { + const descriptor = Object.getOwnPropertyDescriptor(current, key); + if (descriptor) { + if (typeof descriptor.get === "function") { + return descriptor.get.call(self); + } + const value = descriptor.value; + if (typeof value === "function") { + return value.bind(self); + } + return value; + } + current = Object.getPrototypeOf(current); + } + return undefined; + }, + set(target, key, value) { + let current = target; + while (current != null) { + const descriptor = Object.getOwnPropertyDescriptor(current, key); + if (descriptor) { + if (typeof descriptor.set === "function") { + descriptor.set.call(self, value); + return true; + } + if ("value" in descriptor && descriptor.writable) { + self[key] = value; + return true; + } + return false; + } + current = Object.getPrototypeOf(current); + } + self[key] = value; + return true; + } + }); + + Object.defineProperty(self, superCacheKey, { + configurable: true, + enumerable: false, + writable: true, + value: { base: basePrototype, proxy } + }); + + return proxy; + } + }); + }) + )"; + +const char* NSFastEnumerationMethodEncoding() { + static const char* encoding = nullptr; + if (encoding == nullptr) { + struct objc_method_description desc = protocol_getMethodDescription( + @protocol(NSFastEnumeration), @selector(countByEnumeratingWithState:objects:count:), YES, + YES); + encoding = desc.types; + } + return encoding; +} + +void clearPendingException(napi_env env) { + napi_value ignored = nullptr; + napi_get_and_clear_last_exception(env, &ignored); +} + +Protocol* resolveRuntimeProtocol(napi_env env, napi_value protocolValue) { + if (protocolValue == nullptr) { + return nullptr; + } + + ObjCProtocol* wrappedProtocol = nullptr; + if (napi_unwrap(env, protocolValue, (void**)&wrappedProtocol) == napi_ok && + wrappedProtocol != nullptr) { + Protocol* runtimeProtocol = objc_getProtocol(wrappedProtocol->name.c_str()); + if (runtimeProtocol != nullptr) { + return runtimeProtocol; + } + } + + if (Pointer::isInstance(env, protocolValue)) { + Pointer* pointer = Pointer::unwrap(env, protocolValue); + if (pointer != nullptr && pointer->data != nullptr) { + Protocol* runtimeProtocol = (Protocol*)pointer->data; + if (protocol_getName(runtimeProtocol) != nullptr) { + return runtimeProtocol; + } + } + } + + auto protocolFromName = [](const std::string& protocolName) -> Protocol* { + if (protocolName.empty()) { + return nullptr; + } + + Protocol* runtimeProtocol = objc_getProtocol(protocolName.c_str()); + if (runtimeProtocol != nullptr) { + return runtimeProtocol; + } + + const std::string suffix = "Protocol"; + if (protocolName.length() > suffix.length() && + protocolName.rfind(suffix) == protocolName.length() - suffix.length()) { + std::string strippedName = protocolName.substr(0, protocolName.length() - suffix.length()); + return objc_getProtocol(strippedName.c_str()); + } + + return nullptr; + }; + + napi_valuetype valueType = napi_undefined; + napi_typeof(env, protocolValue, &valueType); + if (valueType == napi_string) { + char protocolNameBuffer[512]; + size_t protocolNameLength = 0; + napi_get_value_string_utf8(env, protocolValue, protocolNameBuffer, sizeof(protocolNameBuffer), + &protocolNameLength); + return protocolFromName(std::string(protocolNameBuffer, protocolNameLength)); + } + + if (valueType == napi_object || valueType == napi_function) { + bool hasName = false; + napi_has_named_property(env, protocolValue, "name", &hasName); + if (hasName) { + napi_value protocolNameValue = nullptr; + napi_get_named_property(env, protocolValue, "name", &protocolNameValue); + napi_valuetype protocolNameType = napi_undefined; + napi_typeof(env, protocolNameValue, &protocolNameType); + if (protocolNameType == napi_string) { + char protocolNameBuffer[512]; + size_t protocolNameLength = 0; + napi_get_value_string_utf8(env, protocolNameValue, protocolNameBuffer, + sizeof(protocolNameBuffer), &protocolNameLength); + return protocolFromName(std::string(protocolNameBuffer, protocolNameLength)); + } + } + } + + return nullptr; +} + +napi_value JS_classConformsToProtocolSafe(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value argv[1] = {nullptr}; + napi_value jsThis = nullptr; + napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr); + + Class nativeClass = nullptr; + napi_unwrap(env, jsThis, (void**)&nativeClass); + + bool conforms = false; + if (nativeClass != nullptr && argc > 0) { + Protocol* runtimeProtocol = resolveRuntimeProtocol(env, argv[0]); + if (runtimeProtocol != nullptr) { + conforms = class_conformsToProtocol(nativeClass, runtimeProtocol); + } + } + + napi_value result = nullptr; + napi_get_boolean(env, conforms, &result); + return result; +} + +NSUInteger JS_SymbolIteratorCountByEnumerating(id self, SEL _cmd, NSFastEnumerationState* state, + id __unsafe_unretained stackbuf[], NSUInteger len) { + if (len == 0) { + return 0; + } + + napi_env env = resolveClassBuilderEnv(self); + if (env == nullptr) { + return 0; + } + + ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState == nullptr) { + return 0; + } + + napi_value jsSelf = bridgeState->getObject(env, self, kUnownedObject); + if (jsSelf == nullptr) { + return 0; + } + + napi_value global = nullptr; + napi_value symbolCtor = nullptr; + napi_value symbolIterator = nullptr; + if (napi_get_global(env, &global) != napi_ok || + napi_get_named_property(env, global, "Symbol", &symbolCtor) != napi_ok || + napi_get_named_property(env, symbolCtor, "iterator", &symbolIterator) != napi_ok) { + clearPendingException(env); + return 0; + } + + napi_value iteratorMethod = nullptr; + if (napi_get_property(env, jsSelf, symbolIterator, &iteratorMethod) != napi_ok) { + clearPendingException(env); + return 0; + } + + napi_valuetype iteratorMethodType = napi_undefined; + if (napi_typeof(env, iteratorMethod, &iteratorMethodType) != napi_ok || + iteratorMethodType != napi_function) { + return 0; + } + + napi_value iterator = nullptr; + if (napi_call_function(env, jsSelf, iteratorMethod, 0, nullptr, &iterator) != napi_ok) { + clearPendingException(env); + return 0; + } + + napi_valuetype iteratorType = napi_undefined; + if (napi_typeof(env, iterator, &iteratorType) != napi_ok || + (iteratorType != napi_object && iteratorType != napi_function)) { + return 0; + } + + napi_value nextMethod = nullptr; + if (napi_get_named_property(env, iterator, "next", &nextMethod) != napi_ok) { + clearPendingException(env); + return 0; + } + + napi_valuetype nextType = napi_undefined; + if (napi_typeof(env, nextMethod, &nextType) != napi_ok || nextType != napi_function) { + return 0; + } + + auto callNext = [&](napi_value* nextResult) -> bool { + if (napi_call_function(env, iterator, nextMethod, 0, nullptr, nextResult) != napi_ok) { + clearPendingException(env); + return false; + } + return true; + }; + + auto readDoneFlag = [&](napi_value nextResult, bool* done) -> bool { + napi_value doneValue = nullptr; + if (napi_get_named_property(env, nextResult, "done", &doneValue) != napi_ok) { + clearPendingException(env); + return false; + } + + bool isDone = false; + if (napi_get_value_bool(env, doneValue, &isDone) != napi_ok) { + clearPendingException(env); + return false; + } + + *done = isDone; + return true; + }; + + // Rebuild the iterator from the beginning and skip already yielded values. + // This keeps state handling simple and avoids retaining JS iterators in ObjC. + for (unsigned long skipped = 0; skipped < state->state; skipped++) { + napi_value skippedResult = nullptr; + if (!callNext(&skippedResult)) { + return 0; + } + + bool done = false; + if (!readDoneFlag(skippedResult, &done) || done) { + return 0; + } + } + + const char* objectEncoding = "@"; + auto objectConverter = TypeConv::Make(env, &objectEncoding); + + NSUInteger count = 0; + while (count < len) { + napi_value nextResult = nullptr; + if (!callNext(&nextResult)) { + break; + } + + bool done = false; + if (!readDoneFlag(nextResult, &done)) { + break; + } + + if (done) { + break; + } + + napi_value value = nullptr; + if (napi_get_named_property(env, nextResult, "value", &value) != napi_ok) { + clearPendingException(env); + break; + } + + id nativeValue = nil; + bool shouldFree = false; + bool shouldFreeAny = false; + objectConverter->toNative(env, value, &nativeValue, &shouldFree, &shouldFreeAny); + stackbuf[count++] = nativeValue; + } + + state->itemsPtr = stackbuf; + state->mutationsPtr = &state->extra[0]; + state->extra[0] = 0; + state->state += count; + + return count; +} +} // namespace ClassBuilder::ClassBuilder(napi_env env, napi_value constructor) { this->env = env; @@ -27,17 +404,36 @@ superClassNative = [NSObject class]; } - superclass = bridgeState->classesByPointer[superClassNative]; + auto superClassIt = bridgeState->classesByPointer.find(superClassNative); + if (superClassIt != bridgeState->classesByPointer.end()) { + superclass = superClassIt->second; + } else { + auto mdClassIt = bridgeState->mdClassesByPointer.find(superClassNative); + if (mdClassIt != bridgeState->mdClassesByPointer.end()) { + superclass = bridgeState->getClass(env, mdClassIt->second); + } else { + superclass = nullptr; + } + } napi_value className; - napi_get_named_property(env, constructor, "name", &className); + bool hasNativeClassName = false; + napi_has_named_property(env, constructor, "ObjCClassName", &hasNativeClassName); + if (hasNativeClassName) { + napi_get_named_property(env, constructor, "ObjCClassName", &className); + } else { + napi_get_named_property(env, constructor, "name", &className); + } static char classNameBuf[512]; napi_get_value_string_utf8(env, className, classNameBuf, 512, nullptr); name = classNameBuf; - name += "_"; - name += std::to_string(rand()); - + if (objc_lookUpClass(name.c_str()) != nullptr) { + std::string baseName = name; + do { + name = baseName + "_" + std::to_string(rand()); + } while (objc_lookUpClass(name.c_str()) != nullptr); + } nativeClass = objc_allocateClassPair(superClassNative, name.c_str(), 0); if (nativeClass == nullptr) { @@ -48,6 +444,7 @@ class_addProtocol(nativeClass, @protocol(ObjCBridgeClassBuilderProtocol)); objc_registerClassPair(nativeClass); + registerClassBuilderEnv(nativeClass, env); napi_remove_wrap(env, constructor, nullptr); @@ -61,6 +458,8 @@ } ClassBuilder::~ClassBuilder() { + unregisterClassBuilderEnv(nativeClass); + if (nativeClass != nullptr) { objc_disposeClassPair(nativeClass); napi_delete_reference(env, constructor); @@ -80,43 +479,85 @@ } } -MethodDescriptor* ClassBuilder::lookupMethodDescriptor(std::string& name) { +std::vector ClassBuilder::lookupMethodDescriptors(std::string& name, + bool setter) { + std::vector descriptors; + std::unordered_set selectors; + + auto appendDescriptor = [&](MethodDescriptor* descriptor) { + if (descriptor == nullptr || descriptor->selector == nullptr) { + return; + } + if (!selectors.insert(descriptor->selector).second) { + return; + } + descriptors.emplace_back(descriptor); + }; + // 1. First we look up if there was a custom definition for the method // in ObjCExposedMethods static of the custom class. - auto findExposedMethod = exposedMethods.find(name); - if (findExposedMethod != exposedMethods.end()) { - return &findExposedMethod->second; + if (!setter) { + auto findExposedMethod = exposedMethods.find(name); + if (findExposedMethod != exposedMethods.end()) { + appendDescriptor(&findExposedMethod->second); + return descriptors; + } + + auto findKnownExposedMethod = gKnownExposedMethods.find(name); + if (findKnownExposedMethod != gKnownExposedMethods.end()) { + appendDescriptor(&findKnownExposedMethod->second); + return descriptors; + } } + auto appendMemberDescriptors = [&](ObjCClassMember& member) { + if (setter) { + if (member.methodOrGetter.isProperty && member.setter.selector != nullptr) { + appendDescriptor(&member.setter); + } + return; + } + + appendDescriptor(&member.methodOrGetter); + for (auto& overload : member.overloads) { + appendDescriptor(&overload.method); + } + }; + // 2. Then walk through the class hierarchy and see if we can find the // method in the superclass chain. ObjCClass* currentClass = superclass; while (currentClass != nullptr) { auto findMethod = currentClass->members.find(name); - if (findMethod != currentClass->members.end() && !findMethod->second.classMethod && - !findMethod->second.methodOrGetter.isProperty) { - return &findMethod->second.methodOrGetter; + if (findMethod != currentClass->members.end()) { + appendMemberDescriptors(findMethod->second); + if (!descriptors.empty()) { + return descriptors; + } } currentClass = currentClass->superclass; } // 3. And finally, look into all protocols implemented (directly or // indirectly) and try to find the method there. - std::function & protocols)> processProtocols = - [&](std::unordered_set& protocols) -> MethodDescriptor* { - for (auto protocol : protocols) { - auto findMethod = protocol->members.find(name); - if (findMethod != protocol->members.end() && !findMethod->second.classMethod && - !findMethod->second.methodOrGetter.isProperty) { - return &findMethod->second.methodOrGetter; - } - MethodDescriptor* desc = processProtocols(protocol->protocols); - if (desc != nullptr) return desc; - } - return (MethodDescriptor*)nullptr; - }; + std::function&)> processProtocols = + [&](std::unordered_set& protocols) { + for (auto protocol : protocols) { + auto findMethod = protocol->members.find(name); + if (findMethod != protocol->members.end()) { + appendMemberDescriptors(findMethod->second); + } + processProtocols(protocol->protocols); + } + }; - return processProtocols(protocols); + processProtocols(protocols); + return descriptors; +} + +MethodDescriptor* ClassBuilder::lookupMethodDescriptor(std::string& name, bool setter) { + auto descriptors = lookupMethodDescriptors(name, setter); + return descriptors.empty() ? nullptr : descriptors.front(); } void ClassBuilder::addMethod(std::string& name, MethodDescriptor* desc, napi_value key, @@ -124,12 +565,13 @@ switch (desc->kind) { case kMethodDescEncoding: { const char* encoding = desc->encoding.c_str(); - auto closure = new Closure(encoding, false); + auto closure = new Closure(encoding, false, true); closure->env = env; if (func != nullptr) closure->func = make_ref(env, func); else closure->propertyName = name; + closure->selector = desc->selector; closure->thisConstructor = constructor; class_replaceMethod(nativeClass, desc->selector, (IMP)closure->fnptr, encoding); break; @@ -144,6 +586,7 @@ closure->func = make_ref(env, func); else closure->propertyName = name; + closure->selector = desc->selector; closure->thisConstructor = constructor; class_replaceMethod(nativeClass, desc->selector, (IMP)closure->fnptr, encoding.c_str()); break; @@ -205,7 +648,10 @@ encoding += enctype; } - this->exposedMethods[name] = MethodDescriptor(selector, encoding); + auto descriptor = MethodDescriptor(selector, encoding); + this->exposedMethods[name] = descriptor; + gKnownExposedMethods[name] = descriptor; + gKnownExposedMethods[jsifySelector(name)] = descriptor; } } @@ -244,6 +690,26 @@ uint32_t propertyCount = 0; napi_get_array_length(env, properties, &propertyCount); + napi_value global, objectCtor, getOwnPropertyDescriptor; + napi_get_global(env, &global); + napi_get_named_property(env, global, "Object", &objectCtor); + napi_get_named_property(env, objectCtor, "getOwnPropertyDescriptor", &getOwnPropertyDescriptor); + + bool hasIteratorMethod = false; + napi_value symbolCtor = nullptr; + napi_value symbolIterator = nullptr; + napi_get_named_property(env, global, "Symbol", &symbolCtor); + napi_get_named_property(env, symbolCtor, "iterator", &symbolIterator); + napi_has_own_property(env, prototype, symbolIterator, &hasIteratorMethod); + if (hasIteratorMethod) { + class_addProtocol(nativeClass, @protocol(NSFastEnumeration)); + const char* fastEnumerationEncoding = NSFastEnumerationMethodEncoding(); + if (fastEnumerationEncoding != nullptr) { + class_replaceMethod(nativeClass, @selector(countByEnumeratingWithState:objects:count:), + (IMP)JS_SymbolIteratorCountByEnumerating, fastEnumerationEncoding); + } + } + uint32_t i = 0; while (i < propertyCount) { napi_value property; @@ -251,9 +717,67 @@ static char propertyNameBuf[512]; napi_get_value_string_utf8(env, property, propertyNameBuf, 512, nullptr); std::string name = propertyNameBuf; - MethodDescriptor* desc = lookupMethodDescriptor(name); - if (desc != nullptr) { - addMethod(name, desc, property); + napi_value descriptorArgs[] = {prototype, property}; + napi_value propertyDescriptor; + napi_call_function(env, objectCtor, getOwnPropertyDescriptor, 2, descriptorArgs, + &propertyDescriptor); + + napi_valuetype descriptorType; + napi_typeof(env, propertyDescriptor, &descriptorType); + if (descriptorType == napi_undefined || descriptorType == napi_null) { + i++; + continue; + } + + bool hasValue = false; + napi_has_named_property(env, propertyDescriptor, "value", &hasValue); + if (hasValue) { + napi_value methodFunc; + napi_get_named_property(env, propertyDescriptor, "value", &methodFunc); + napi_valuetype funcType = napi_undefined; + napi_typeof(env, methodFunc, &funcType); + if (funcType == napi_function) { + auto descriptors = lookupMethodDescriptors(name); + for (MethodDescriptor* descriptor : descriptors) { + if (descriptor != nullptr) { + addMethod(name, descriptor, property, methodFunc); + } + } + } + } + + bool hasGetter = false; + napi_has_named_property(env, propertyDescriptor, "get", &hasGetter); + if (hasGetter) { + napi_value getterFunc; + napi_get_named_property(env, propertyDescriptor, "get", &getterFunc); + napi_valuetype getterType = napi_undefined; + napi_typeof(env, getterFunc, &getterType); + if (getterType == napi_function) { + auto getterDescs = lookupMethodDescriptors(name); + for (MethodDescriptor* getterDesc : getterDescs) { + if (getterDesc != nullptr) { + addMethod(name, getterDesc, property, getterFunc); + } + } + } + } + + bool hasSetter = false; + napi_has_named_property(env, propertyDescriptor, "set", &hasSetter); + if (hasSetter) { + napi_value setterFunc; + napi_get_named_property(env, propertyDescriptor, "set", &setterFunc); + napi_valuetype setterType = napi_undefined; + napi_typeof(env, setterFunc, &setterType); + if (setterType == napi_function) { + auto setterDescs = lookupMethodDescriptors(name, true); + for (MethodDescriptor* setterDesc : setterDescs) { + if (setterDesc != nullptr) { + addMethod(name, setterDesc, property, setterFunc); + } + } + } } i++; } @@ -290,16 +814,90 @@ return nullptr; } - // Create a unique class name + if (class_conformsToProtocol(baseNativeClass, @protocol(ObjCBridgeClassBuilderProtocol))) { + napi_throw_error(env, nullptr, "Cannot extend an already extended class."); + return nullptr; + } + + napi_value options = nullptr; + bool hasOptionsObject = false; + if (argc >= 2) { + napi_valuetype secondArgType; + napi_typeof(env, args[1], &secondArgType); + hasOptionsObject = secondArgType == napi_object; + if (hasOptionsObject) { + options = args[1]; + } + } + + bool hasOwnOverrides = false; + napi_value overridePropertyNames = nullptr; + napi_get_all_property_names(env, args[0], napi_key_own_only, napi_key_skip_symbols, + napi_key_numbers_to_strings, &overridePropertyNames); + uint32_t overridePropertyCount = 0; + napi_get_array_length(env, overridePropertyNames, &overridePropertyCount); + hasOwnOverrides = overridePropertyCount > 0; + + bool hasExposedMethodsOption = false; + bool hasProtocolsOption = false; + if (hasOptionsObject) { + napi_has_named_property(env, options, "exposedMethods", &hasExposedMethodsOption); + napi_has_named_property(env, options, "protocols", &hasProtocolsOption); + } + + bool shouldReuseExistingClass = false; + Class existingExternalClass = nullptr; + + // Create a class name. napi_value baseClassName; napi_get_named_property(env, thisArg, "name", &baseClassName); static char baseClassNameBuf[512]; napi_get_value_string_utf8(env, baseClassName, baseClassNameBuf, 512, nullptr); - std::string newClassName = baseClassNameBuf; - newClassName += "_Extended_"; - newClassName += std::to_string(rand()); + std::string requestedName; + if (hasOptionsObject) { + bool hasCustomName = false; + napi_has_named_property(env, options, "name", &hasCustomName); + if (hasCustomName) { + napi_value customNameValue = nullptr; + napi_get_named_property(env, options, "name", &customNameValue); + napi_valuetype customNameType = napi_undefined; + napi_typeof(env, customNameValue, &customNameType); + if (customNameType == napi_string) { + static char customNameBuf[512]; + size_t customNameLen = 0; + napi_get_value_string_utf8(env, customNameValue, customNameBuf, sizeof(customNameBuf), + &customNameLen); + if (customNameLen > 0) { + requestedName.assign(customNameBuf, customNameLen); + } + } + } + } + std::string newClassName; + if (!requestedName.empty()) { + newClassName = requestedName; + Class existingClass = objc_lookUpClass(newClassName.c_str()); + if (existingClass != nullptr && + class_conformsToProtocol(existingClass, @protocol(ObjCBridgeClassBuilderProtocol))) { + size_t suffix = 1; + std::string candidate; + do { + candidate = requestedName + std::to_string(suffix++); + } while (objc_lookUpClass(candidate.c_str()) != nullptr); + newClassName = candidate; + } else if (existingClass != nullptr && !hasOwnOverrides && !hasExposedMethodsOption && + !hasProtocolsOption) { + // Name-only extensions should resolve to an existing external class when present. + shouldReuseExistingClass = true; + existingExternalClass = existingClass; + } + } else { + newClassName = baseClassNameBuf; + newClassName += "_Extended_"; + newClassName += std::to_string(rand()); + } // Create the new constructor function that extends the base napi_value newConstructor; napi_define_class(env, newClassName.c_str(), newClassName.length(), JS_BridgedConstructor, @@ -308,46 +906,65 @@ // Set up JavaScript inheritance from the base class napi_inherits(env, newConstructor, thisArg); + napi_value safeConformsToProtocol = nullptr; + napi_create_function(env, "conformsToProtocol", NAPI_AUTO_LENGTH, JS_classConformsToProtocolSafe, + nullptr, &safeConformsToProtocol); + napi_set_named_property(env, newConstructor, "conformsToProtocol", safeConformsToProtocol); + // Get prototype for adding methods - napi_value newPrototype; + napi_value newPrototype, basePrototype; napi_get_named_property(env, newConstructor, "prototype", &newPrototype); + napi_get_named_property(env, thisArg, "prototype", &basePrototype); + + // Copy methods and accessors preserving descriptors. + napi_value global, objectCtor, getOwnPropertyDescriptors, defineProperties; + napi_get_global(env, &global); + napi_get_named_property(env, global, "Object", &objectCtor); + napi_get_named_property(env, objectCtor, "getOwnPropertyDescriptors", &getOwnPropertyDescriptors); + napi_get_named_property(env, objectCtor, "defineProperties", &defineProperties); + + napi_value descriptors; + napi_call_function(env, objectCtor, getOwnPropertyDescriptors, 1, args, &descriptors); + + napi_value defineArgs[] = {newPrototype, descriptors}; + napi_call_function(env, objectCtor, defineProperties, 2, defineArgs, nullptr); + + napi_value installSuperAccessor = nullptr; + napi_value installSuperScript = nullptr; + napi_create_string_utf8(env, kInstallSuperAccessorSource, NAPI_AUTO_LENGTH, &installSuperScript); + napi_run_script(env, installSuperScript, &installSuperAccessor); + napi_value superArgs[] = {newPrototype, basePrototype}; + napi_call_function(env, global, installSuperAccessor, 2, superArgs, nullptr); + + // Handle optional second parameter for metadata/options + if (hasOptionsObject) { + napi_value exposedMethods; + bool hasExposedMethods = false; + napi_has_named_property(env, options, "exposedMethods", &hasExposedMethods); + + if (hasExposedMethods) { + napi_get_named_property(env, options, "exposedMethods", &exposedMethods); + napi_set_named_property(env, newConstructor, "ObjCExposedMethods", exposedMethods); + } - // Add methods from the first parameter to the prototype - napi_value methodNames; - napi_get_all_property_names(env, args[0], napi_key_own_only, napi_key_skip_symbols, - napi_key_numbers_to_strings, &methodNames); - - uint32_t methodCount = 0; - napi_get_array_length(env, methodNames, &methodCount); - - for (uint32_t i = 0; i < methodCount; i++) { - napi_value methodName, methodFunc; - napi_get_element(env, methodNames, i, &methodName); - - static char methodNameBuf[512]; - napi_get_value_string_utf8(env, methodName, methodNameBuf, 512, nullptr); - std::string name = methodNameBuf; - - napi_get_named_property(env, args[0], name.c_str(), &methodFunc); + napi_value protocols; + bool hasProtocols = false; + napi_has_named_property(env, options, "protocols", &hasProtocols); - // Add the method to the prototype - napi_set_named_property(env, newPrototype, name.c_str(), methodFunc); + if (hasProtocols) { + napi_get_named_property(env, options, "protocols", &protocols); + napi_set_named_property(env, newConstructor, "ObjCProtocols", protocols); + } } - // Handle optional second parameter for protocols - if (argc >= 2) { - napi_valuetype secondArgType; - napi_typeof(env, args[1], &secondArgType); - if (secondArgType == napi_object) { - napi_value protocols; - bool hasProtocols = false; - napi_has_named_property(env, args[1], "protocols", &hasProtocols); - - if (hasProtocols) { - napi_get_named_property(env, args[1], "protocols", &protocols); - napi_set_named_property(env, newConstructor, "ObjCProtocols", protocols); - } - } + napi_value classNameValue = nullptr; + napi_create_string_utf8(env, newClassName.c_str(), newClassName.length(), &classNameValue); + napi_set_named_property(env, newConstructor, "ObjCClassName", classNameValue); + + if (shouldReuseExistingClass && existingExternalClass != nullptr) { + napi_remove_wrap(env, newConstructor, nullptr); + napi_wrap(env, newConstructor, (void*)existingExternalClass, nullptr, nullptr, nullptr); + return newConstructor; } // Use ClassBuilder to create the native class and bridge the methods diff --git a/NativeScript/ffi/ClassMember.h b/NativeScript/ffi/ClassMember.h index a8808dab..aea44b72 100644 --- a/NativeScript/ffi/ClassMember.h +++ b/NativeScript/ffi/ClassMember.h @@ -1,7 +1,9 @@ #ifndef BRIDGED_METHOD_H #define BRIDGED_METHOD_H +#include #include +#include #include "Cif.h" #include "objc/runtime.h" @@ -22,8 +24,15 @@ class MethodDescriptor { MethodDescriptorKind kind; MDSectionOffset signatureOffset; + uint8_t dispatchFlags = 0; std::string encoding; bool isProperty = false; + bool dispatchLookupCached = false; + uint64_t dispatchLookupSignatureHash = 0; + uint8_t dispatchLookupFlags = 0; + uint64_t dispatchId = 0; + void* preparedInvoker = nullptr; + void* napiInvoker = nullptr; MethodDescriptor() {} @@ -49,6 +58,16 @@ typedef std::unordered_map ObjCClassMemberMap; class ObjCClass; +struct ObjCClassMemberOverload { + MethodDescriptor method; + Cif* cif = nullptr; + + ObjCClassMemberOverload(SEL selector, MDSectionOffset offset, uint8_t dispatchFlags) + : method(selector, offset) { + method.dispatchFlags = dispatchFlags; + } +}; + class ObjCClassMember { public: static void defineMembers(napi_env env, ObjCClassMemberMap& memberMap, @@ -58,14 +77,19 @@ class ObjCClassMember { static napi_value jsCall(napi_env env, napi_callback_info cbinfo); static napi_value jsCallInit(napi_env env, napi_callback_info cbinfo); static napi_value jsGetter(napi_env env, napi_callback_info cbinfo); + static napi_value jsReadOnlySetter(napi_env env, napi_callback_info cbinfo); static napi_value jsSetter(napi_env env, napi_callback_info cbinfo); + void addOverload(SEL selector, MDSectionOffset offset, uint8_t dispatchFlags); ObjCClassMember(ObjCBridgeState* bridgeState, SEL selector, MDSectionOffset offset, MDMemberFlag flags) : bridgeState(bridgeState), methodOrGetter(MethodDescriptor(selector, offset)), returnOwned((flags & metagen::mdMemberReturnOwned) != 0), - classMethod((flags & metagen::mdMemberStatic) != 0) {} + classMethod((flags & metagen::mdMemberStatic) != 0), + cls(nullptr) { + methodOrGetter.dispatchFlags = returnOwned ? 1 : 0; + } ObjCClassMember(ObjCBridgeState* bridgeState, SEL getterSelector, SEL setterSelector, MDSectionOffset getterOffset, @@ -74,9 +98,12 @@ class ObjCClassMember { methodOrGetter(MethodDescriptor(getterSelector, getterOffset)), setter(MethodDescriptor(setterSelector, setterOffset)), returnOwned((flags & metagen::mdMemberReturnOwned) != 0), - classMethod((flags & metagen::mdMemberStatic) != 0) { + classMethod((flags & metagen::mdMemberStatic) != 0), + cls(nullptr) { methodOrGetter.isProperty = true; setter.isProperty = true; + methodOrGetter.dispatchFlags = returnOwned ? 1 : 0; + setter.dispatchFlags = 0; } ObjCBridgeState* bridgeState; @@ -87,6 +114,7 @@ class ObjCClassMember { bool returnOwned; bool classMethod; ObjCClass* cls; + std::vector overloads; }; } // namespace nativescript diff --git a/NativeScript/ffi/ClassMember.mm b/NativeScript/ffi/ClassMember.mm index a81d6d87..e1a87776 100644 --- a/NativeScript/ffi/ClassMember.mm +++ b/NativeScript/ffi/ClassMember.mm @@ -2,12 +2,22 @@ #import #include #include +#include +#include +#include +#include #include +#include +#include +#include #include "ClassBuilder.h" +#include "Closure.h" #include "MetadataReader.h" #include "ObjCBridge.h" +#include "SignatureDispatch.h" #include "TypeConv.h" #include "Util.h" +#include "ffi/Block.h" #include "ffi/Class.h" #include "ffi/NativeScriptException.h" #include "js_native_api.h" @@ -18,12 +28,22 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) { napi_value jsThis; - napi_get_cb_info(env, cbinfo, nullptr, nullptr, &jsThis, nullptr); + ObjCClassMember* method = nullptr; + napi_get_cb_info(env, cbinfo, nullptr, nullptr, &jsThis, (void**)&method); - id self; - napi_unwrap(env, jsThis, (void**)&self); + id self = nil; + napi_status unwrapStatus = napi_unwrap(env, jsThis, (void**)&self); + if ((unwrapStatus != napi_ok || self == nil) && method != nullptr && method->cls != nullptr && + method->cls->nativeClass != nil) { + self = (id)method->cls->nativeClass; + } + if (self == nil) { + napi_throw_error(env, "NativeScriptException", + "There was no native class counterpart to the JavaScript constructor."); + return nullptr; + } - bool supercall = class_conformsToProtocol(self, @protocol(ObjCBridgeClassBuilderProtocol)); + bool supercall = class_conformsToProtocol((Class)self, @protocol(ObjCBridgeClassBuilderProtocol)); if (supercall) { ObjCBridgeState* state = ObjCBridgeState::InstanceData(env); @@ -45,6 +65,25 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) { bool next = true; + auto hasOwnNamedProperty = [&](napi_value object, const char* propertyName) { + napi_value propertyKey = nullptr; + napi_create_string_utf8(env, propertyName, NAPI_AUTO_LENGTH, &propertyKey); + bool hasOwn = false; + napi_has_own_property(env, object, propertyKey, &hasOwn); + return hasOwn; + }; + + auto tryGetSuperclassMember = [&](const std::string& name) -> ObjCClassMember* { + if (cls == nullptr || cls->superclass == nullptr) { + return nullptr; + } + auto it = cls->superclass->members.find(name); + if (it == cls->superclass->members.end()) { + return nullptr; + } + return &it->second; + }; + while (next) { auto flags = bridgeState->metadata->getMemberFlag(offset); @@ -83,27 +122,50 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) { offset += sizeof(MDSectionOffset); // setterSignature } - if (memberMap.contains(name)) { - memberMap.erase(name); + bool hasProperty = false; + napi_has_named_property(env, jsObject, name, &hasProperty); + bool hasOwnProperty = hasOwnNamedProperty(jsObject, name); + bool inheritedProperty = hasProperty && !hasOwnProperty; + + ObjCClassMember* superMember = tryGetSuperclassMember(name); + + if (inheritedProperty && superMember != nullptr && superMember->methodOrGetter.isProperty) { + bool superReadonly = superMember->setter.selector == nullptr; + SEL getterSel = sel_registerName(getterSelector); + SEL setterSel = readonly ? nullptr : sel_registerName(setterSelector); + bool sameGetter = superMember->methodOrGetter.selector == getterSel; + bool sameSetter = superMember->setter.selector == setterSel; + + if ((!superReadonly && readonly) || + (superReadonly == readonly && sameGetter && (readonly || sameSetter))) { + continue; + } + } else if (inheritedProperty && readonly) { + continue; } - const auto& kv = memberMap.emplace( - name, - ObjCClassMember(bridgeState, sel_registerName(getterSelector), - !readonly ? sel_registerName(setterSelector) : nullptr, - getterSignature + bridgeState->metadata->signaturesOffset, - !readonly ? setterSignature + bridgeState->metadata->signaturesOffset : 0, - flags)); + auto updatedMember = ObjCClassMember( + bridgeState, sel_registerName(getterSelector), + !readonly ? sel_registerName(setterSelector) : nullptr, + getterSignature + bridgeState->metadata->signaturesOffset, + !readonly ? setterSignature + bridgeState->metadata->signaturesOffset : 0, flags); + auto memberIt = memberMap.find(name); + if (memberIt != memberMap.end()) { + memberIt->second = updatedMember; + } else { + const auto& inserted = memberMap.emplace(name, updatedMember); + memberIt = inserted.first; + } napi_property_descriptor property = { .utf8name = name, .name = nil, .method = nil, .getter = jsGetter, - .setter = readonly ? nil : jsSetter, + .setter = readonly ? jsReadOnlySetter : jsSetter, .value = nil, .attributes = (napi_property_attributes)(napi_configurable | napi_enumerable), - .data = &kv.first->second, + .data = &memberIt->second, }; napi_define_properties(env, jsObject, 1, &property); @@ -114,16 +176,78 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) { offset += sizeof(MDSectionOffset); // signature auto name = jsifySelector(selector); - bool hasProperty = false; napi_has_named_property(env, jsObject, name.c_str(), &hasProperty); - if (hasProperty && name != "init") { + bool hasOwnProperty = hasOwnNamedProperty(jsObject, name.c_str()); + bool inheritedProperty = hasProperty && !hasOwnProperty && name != "init"; + + SEL methodSelector = sel_registerName(selector); + MDSectionOffset signatureOffset = signature + bridgeState->metadata->signaturesOffset; + ObjCClassMember* superMember = tryGetSuperclassMember(name); + bool selectorExistsInSuper = false; + if (superMember != nullptr && !superMember->methodOrGetter.isProperty) { + selectorExistsInSuper = superMember->methodOrGetter.selector == methodSelector; + if (!selectorExistsInSuper) { + for (const auto& overload : superMember->overloads) { + if (overload.method.selector == methodSelector) { + selectorExistsInSuper = true; + break; + } + } + } + } + + const bool keepInheritedMethod = + name == "alloc" || name == "toString" || name == "superclass"; + if (inheritedProperty && selectorExistsInSuper && !keepInheritedMethod) { + continue; + } + + auto memberIt = memberMap.find(name); + ObjCClassMember* member = nullptr; + if (memberIt != memberMap.end()) { + if (memberIt->second.methodOrGetter.isProperty) { + continue; + } + memberIt->second.addOverload(methodSelector, signatureOffset, + (flags & metagen::mdMemberReturnOwned) != 0 ? 1 : 0); + member = &memberIt->second; + } else if (inheritedProperty && superMember != nullptr && + !superMember->methodOrGetter.isProperty) { + const auto& inserted = memberMap.emplace( + name, ObjCClassMember(bridgeState, superMember->methodOrGetter.selector, + superMember->methodOrGetter.signatureOffset, flags)); + member = &inserted.first->second; + for (const auto& overload : superMember->overloads) { + member->addOverload(overload.method.selector, overload.method.signatureOffset, + overload.method.dispatchFlags); + } + member->addOverload(methodSelector, signatureOffset, + (flags & metagen::mdMemberReturnOwned) != 0 ? 1 : 0); + } else { + const auto& inserted = memberMap.emplace( + name, ObjCClassMember(bridgeState, methodSelector, signatureOffset, flags)); + member = &inserted.first->second; + } + + if (member == nullptr) { + continue; + } + + if (cls != nullptr) { + member->cls = cls; + } + + if (hasOwnProperty && name != "init") { + if ((flags & mdMemberIsInit) != 0) { + member->cls = cls; + } continue; } - const auto& kv = memberMap.emplace( - name, ObjCClassMember(bridgeState, sel_registerName(selector), - signature + bridgeState->metadata->signaturesOffset, flags)); + if (inheritedProperty && !selectorExistsInSuper && superMember == nullptr && name != "init") { + continue; + } napi_property_descriptor property = { .utf8name = name.c_str(), @@ -134,11 +258,11 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) { .value = nil, .attributes = (napi_property_attributes)(napi_configurable | napi_writable | napi_enumerable), - .data = &kv.first->second, + .data = member, }; if ((flags & mdMemberIsInit) != 0) { - kv.first->second.cls = cls; + member->cls = cls; } if (name == "alloc") { @@ -150,13 +274,97 @@ napi_value JS_NSObject_alloc(napi_env env, napi_callback_info cbinfo) { } } -inline bool objcNativeCall(napi_env env, Cif* cif, id self, void** avalues, void* rvalue) { - bool classMethod = class_isMetaClass(object_getClass(self)); +void ObjCClassMember::addOverload(SEL selector, MDSectionOffset offset, uint8_t dispatchFlags) { + if (methodOrGetter.selector == selector) { + return; + } + + for (const auto& overload : overloads) { + if (overload.method.selector == selector) { + return; + } + } + + overloads.emplace_back(selector, offset, dispatchFlags); +} + +inline bool tryObjCNapiDispatch(napi_env env, Cif* cif, id self, bool classMethod, SEL selector, + MethodDescriptor* descriptor, uint8_t dispatchFlags, + const napi_value* argv, void* rvalue, bool* didInvoke) { + if (didInvoke != nullptr) { + *didInvoke = false; + } + + if (cif == nullptr || cif->signatureHash == 0 || cif->skipGeneratedNapiDispatch) { + return true; + } + + Class receiverClass = classMethod ? (Class)self : object_getClass(self); + const bool supercall = + receiverClass != nil && + class_conformsToProtocol(receiverClass, @protocol(ObjCBridgeClassBuilderProtocol)); + if (supercall) { + return true; + } + + if (descriptor != nullptr) { + if (!descriptor->dispatchLookupCached || + descriptor->dispatchLookupSignatureHash != cif->signatureHash || + descriptor->dispatchLookupFlags != dispatchFlags) { + descriptor->dispatchLookupSignatureHash = cif->signatureHash; + descriptor->dispatchLookupFlags = dispatchFlags; + descriptor->dispatchId = composeSignatureDispatchId( + cif->signatureHash, SignatureCallKind::ObjCMethod, dispatchFlags); + descriptor->preparedInvoker = + reinterpret_cast(lookupObjCPreparedInvoker(descriptor->dispatchId)); + descriptor->napiInvoker = + reinterpret_cast(lookupObjCNapiInvoker(descriptor->dispatchId)); + descriptor->dispatchLookupCached = true; + } + } + + auto invoker = descriptor != nullptr + ? reinterpret_cast(descriptor->napiInvoker) + : lookupObjCNapiInvoker(composeSignatureDispatchId( + cif->signatureHash, SignatureCallKind::ObjCMethod, dispatchFlags)); + if (invoker == nullptr) { + return true; + } + + @try { + if (!invoker(env, cif, (void*)objc_msgSend, self, selector, argv, rvalue)) { + return false; + } + } @catch (NSException* exception) { + std::string message = exception.description.UTF8String; + nativescript::NativeScriptException nativeScriptException(message); + nativeScriptException.ReThrowToJS(env); + return false; + } + + if (didInvoke != nullptr) { + *didInvoke = true; + } + return true; +} + +inline bool objcNativeCall(napi_env env, Cif* cif, id self, bool classMethod, + MethodDescriptor* descriptor, uint8_t dispatchFlags, void** avalues, + void* rvalue) { + SEL selector = descriptor != nullptr + ? descriptor->selector + : (avalues != nullptr && cif->cif.nargs >= 2 ? *((SEL*)avalues[1]) : nullptr); + + Class receiverClass = nil; + if (classMethod) { + receiverClass = (Class)self; + } else { + receiverClass = object_getClass(self); + } - bool supercall = classMethod - ? class_conformsToProtocol(self, @protocol(ObjCBridgeClassBuilderProtocol)) - : class_conformsToProtocol(object_getClass(self), - @protocol(ObjCBridgeClassBuilderProtocol)); + bool supercall = + receiverClass != nil && + class_conformsToProtocol(receiverClass, @protocol(ObjCBridgeClassBuilderProtocol)); if (supercall && classMethod) { ObjCBridgeState* state = ObjCBridgeState::InstanceData(env); @@ -170,6 +378,32 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, void** avalues, void @try { if (!supercall) { + if (cif != nullptr && cif->signatureHash != 0) { + if (descriptor != nullptr && + (!descriptor->dispatchLookupCached || + descriptor->dispatchLookupSignatureHash != cif->signatureHash || + descriptor->dispatchLookupFlags != dispatchFlags)) { + descriptor->dispatchLookupSignatureHash = cif->signatureHash; + descriptor->dispatchLookupFlags = dispatchFlags; + descriptor->dispatchId = composeSignatureDispatchId( + cif->signatureHash, SignatureCallKind::ObjCMethod, dispatchFlags); + descriptor->preparedInvoker = + reinterpret_cast(lookupObjCPreparedInvoker(descriptor->dispatchId)); + descriptor->napiInvoker = + reinterpret_cast(lookupObjCNapiInvoker(descriptor->dispatchId)); + descriptor->dispatchLookupCached = true; + } + + auto invoker = descriptor != nullptr + ? reinterpret_cast(descriptor->preparedInvoker) + : lookupObjCPreparedInvoker(composeSignatureDispatchId( + cif->signatureHash, SignatureCallKind::ObjCMethod, dispatchFlags)); + if (invoker != nullptr) { + invoker((void*)objc_msgSend, avalues, rvalue); + return true; + } + } + #if defined(__x86_64__) if (isStret) { ffi_call(&cif->cif, FFI_FN(objc_msgSend_stret), rvalue, avalues); @@ -180,7 +414,9 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, void** avalues, void ffi_call(&cif->cif, FFI_FN(objc_msgSend), rvalue, avalues); #endif } else { - struct objc_super superobj = {self, class_getSuperclass(object_getClass(self))}; + Class superClass = classMethod ? class_getSuperclass(object_getClass((id)receiverClass)) + : class_getSuperclass(receiverClass); + struct objc_super superobj = {self, superClass}; auto superobjPtr = &superobj; avalues[0] = (void*)&superobjPtr; #if defined(__x86_64__) @@ -206,6 +442,10 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, void** avalues, void // Utility function to check if a JS value can be converted to a specific type bool canConvertToType(napi_env env, napi_value value, std::shared_ptr typeConv) { + if (typeConv == nullptr) { + return false; + } + if (value == nullptr) { return true; // null/undefined can convert to most types } @@ -224,8 +464,19 @@ bool canConvertToType(napi_env env, napi_value value, std::shared_ptr case mdTypeChar: case mdTypeUChar: + return jsType == napi_boolean || jsType == napi_number || jsType == napi_bigint; + case mdTypeSShort: + return jsType == napi_number || jsType == napi_bigint; + case mdTypeUShort: + if (jsType == napi_string) { + size_t len = 0; + napi_get_value_string_utf16(env, value, nullptr, 0, &len); + return len == 1; + } + return jsType == napi_number || jsType == napi_bigint; + case mdTypeSInt: case mdTypeUInt: case mdTypeSLong: @@ -236,7 +487,24 @@ bool canConvertToType(napi_env env, napi_value value, std::shared_ptr case mdTypeDouble: return jsType == napi_number || jsType == napi_bigint; + case mdTypeString: + return jsType == napi_string || jsType == napi_object; + + case mdTypeAnyObject: + return jsType == napi_object || jsType == napi_function || jsType == napi_string || + jsType == napi_number || jsType == napi_boolean; + + case mdTypeClass: + case mdTypeClassObject: + case mdTypeProtocolObject: + return jsType == napi_function || jsType == napi_object; + case mdTypeInstanceObject: + // ObjC object conversion can box JS primitives (e.g. number -> NSNumber) + // and map plain JS objects/arrays to Foundation containers. + return jsType == napi_object || jsType == napi_string || jsType == napi_number || + jsType == napi_boolean || jsType == napi_bigint; + case mdTypeNSStringObject: case mdTypeNSMutableStringObject: { if (jsType == napi_string) { @@ -263,53 +531,256 @@ bool canConvertToType(napi_env env, napi_value value, std::shared_ptr case mdTypePointer: case mdTypeOpaquePointer: - return jsType == napi_object || jsType == napi_bigint || jsType == napi_string; + return jsType == napi_object || jsType == napi_function || jsType == napi_bigint || + jsType == napi_string; case mdTypeStruct: return jsType == napi_object; case mdTypeBlock: - return jsType == napi_function; + case mdTypeFunctionPointer: + return jsType == napi_function || jsType == napi_null || jsType == napi_undefined; default: - return true; // For unknown types, assume compatible + return false; + } +} + +inline bool selectorEndsWith(SEL selector, const char* suffix) { + if (selector == nullptr || suffix == nullptr) { + return false; + } + + const char* selectorName = sel_getName(selector); + if (selectorName == nullptr) { + return false; + } + + size_t selectorLength = strlen(selectorName); + size_t suffixLength = strlen(suffix); + if (selectorLength < suffixLength) { + return false; + } + + return strcmp(selectorName + selectorLength - suffixLength, suffix) == 0; +} + +inline bool isNSErrorOutMethodSignature(SEL selector, Cif* cif) { + if (cif == nullptr || cif->argc == 0 || cif->argTypes.empty()) { + return false; + } + + if (!selectorEndsWith(selector, "error:")) { + return false; } + + auto lastArgType = cif->argTypes[cif->argc - 1]; + return lastArgType != nullptr && lastArgType->kind == mdTypePointer; +} + +inline void throwArgumentsCountError(napi_env env, size_t actualCount, size_t expectedCount) { + std::string message = "Actual arguments count: \"" + std::to_string(actualCount) + + "\". Expected: \"" + std::to_string(expectedCount) + "\"."; + napi_throw_error(env, "NativeScriptException", message.c_str()); } -// Find the best initializer for a class given JS arguments -ObjCClassMember* findInitializerForArgs(napi_env env, ObjCClassMemberMap* initializers, size_t argc, - napi_value* argv) { - std::vector candidates; +bool isPlainObjectLiteral(napi_env env, napi_value value) { + if (value == nullptr) { + return false; + } + + napi_valuetype type = napi_undefined; + napi_typeof(env, value, &type); + if (type != napi_object) { + return false; + } + + bool isArray = false; + napi_is_array(env, value, &isArray); + if (isArray) { + return false; + } + + void* wrapped = nullptr; + if (napi_unwrap(env, value, &wrapped) == napi_ok && wrapped != nullptr) { + return false; + } + + return true; +} + +std::string lowerFirst(std::string value) { + if (!value.empty()) { + value[0] = (char)std::tolower(value[0]); + } + return value; +} + +std::vector selectorTokens(const char* selectorName) { + std::vector tokens; + if (selectorName == nullptr) { + return tokens; + } + + std::string selector(selectorName); + size_t start = 0; + while (start < selector.size()) { + size_t colon = selector.find(':', start); + if (colon == std::string::npos) { + break; + } + tokens.push_back(selector.substr(start, colon - start)); + start = colon + 1; + } + + if (tokens.empty()) { + return tokens; + } + + std::string& first = tokens[0]; + if (first.rfind("initWith", 0) == 0 && first.size() > 8) { + first = first.substr(8); + } else if (first.rfind("init", 0) == 0 && first.size() > 4) { + first = first.substr(4); + } + first = lowerFirst(first); + + for (size_t i = 1; i < tokens.size(); ++i) { + tokens[i] = lowerFirst(tokens[i]); + } + + return tokens; +} + +bool tryResolveTokenArgs(napi_env env, const char* selectorName, napi_value tokenObject, + std::vector* resolvedArgs) { + resolvedArgs->clear(); + + std::vector tokens = selectorTokens(selectorName); + if (tokens.empty()) { + return false; + } + + for (const std::string& token : tokens) { + if (token.empty()) { + return false; + } + bool hasProperty = false; + napi_has_named_property(env, tokenObject, token.c_str(), &hasProperty); + if (!hasProperty) { + return false; + } + napi_value tokenValue = nullptr; + napi_get_named_property(env, tokenObject, token.c_str(), &tokenValue); + resolvedArgs->push_back(tokenValue); + } + + return !resolvedArgs->empty(); +} + +Cif* resolveInitCif(napi_env env, ObjCClassMember* candidate, Class nativeClass) { + if (candidate == nullptr || candidate->bridgeState == nullptr) { + return nullptr; + } + + if (nativeClass != nil) { + Method method = class_getInstanceMethod(nativeClass, candidate->methodOrGetter.selector); + if (method == nullptr) { + return nullptr; + } + Cif* runtimeCif = candidate->bridgeState->getMethodCif(env, method); + Cif* metadataCif = + candidate->bridgeState->getMethodCif(env, candidate->methodOrGetter.signatureOffset); + + // Metadata signatures currently under-specify some variadic Objective-C methods + // (e.g. initWithTitle:...otherButtonTitles:), so fall back to runtime encoding there. + if (metadataCif == nullptr) { + return runtimeCif; + } + if (metadataCif->isVariadic || + (metadataCif->argc == 0 && runtimeCif != nullptr && runtimeCif->argc > 0)) { + return runtimeCif != nullptr ? runtimeCif : metadataCif; + } + return metadataCif; + } + + return candidate->bridgeState->getMethodCif(env, candidate->methodOrGetter.signatureOffset); +} + +// Find the best initializer for a class given JS arguments. +ObjCClassMember* findInitializerForArgs(napi_env env, ObjCClassMemberMap* initializers, + Class nativeClass, size_t argc, napi_value* argv, + std::vector* selectedArgs) { + if (initializers == nullptr) { + napi_throw_error(env, "NativeScriptException", + "No Objective-C metadata available for constructor invocation."); + return nullptr; + } + + if (argc > 0 && argv == nullptr) { + napi_throw_error(env, "NativeScriptException", + "Invalid constructor arguments for initializer resolution."); + return nullptr; + } + + struct Candidate { + ObjCClassMember* member; + Cif* cif; + std::vector args; + int bonus; + }; + + std::vector candidates; + const bool hasTokenObject = argc == 1 && argv != nullptr && isPlainObjectLiteral(env, argv[0]); + napi_value tokenObject = hasTokenObject ? argv[0] : nullptr; // First pass: find initializers with matching argument count for (auto& pair : *initializers) { auto* candidate = &pair.second; - const char* name = sel_getName(candidate->methodOrGetter.selector); - // NSLog(@"Checking initializer: %s", name); - if (name[0] != 'i' || name[1] != 'n' || name[2] != 'i' || name[3] != 't') { + if (candidate == nullptr || candidate->methodOrGetter.selector == nullptr) { continue; } - Cif* cif = candidate->cif; - if (!cif) { - // Need to get the CIF to check argument count - cif = const_cast(candidate)->bridgeState->getMethodCif( - env, candidate->methodOrGetter.signatureOffset); + + const std::string& memberName = pair.first; + if (memberName.rfind("init", 0) != 0) { + continue; } - // Match argument count (cif->argc excludes self and selector) - if (cif->argc == argc) { - bool canInvoke = true; + Cif* cif = resolveInitCif(env, candidate, nativeClass); + if (cif == nullptr) { + continue; + } + candidate->cif = cif; - // Check if all arguments can be converted to the expected types - for (size_t i = 0; i < argc; ++i) { - if (!canConvertToType(env, argv[i], cif->argTypes[i])) { - canInvoke = false; - break; + auto tryAddCandidate = [&](const std::vector& callArgs, int bonus) { + if (cif->argc != callArgs.size()) { + return; + } + + for (size_t i = 0; i < callArgs.size(); ++i) { + if (!canConvertToType(env, callArgs[i], cif->argTypes[i])) { + return; } } - if (canInvoke) { - candidates.push_back(candidate); + candidates.push_back(Candidate{candidate, cif, callArgs, bonus}); + }; + + std::vector regularArgs(argc); + for (size_t i = 0; i < argc; ++i) { + regularArgs[i] = argv[i]; + } + tryAddCandidate(regularArgs, 0); + + if (hasTokenObject) { + const char* selectorName = sel_getName(candidate->methodOrGetter.selector); + if (selectorName == nullptr) { + continue; + } + + std::vector tokenArgs; + if (tryResolveTokenArgs(env, selectorName, tokenObject, &tokenArgs)) { + tryAddCandidate(tokenArgs, 100); } } } @@ -319,43 +790,391 @@ bool canConvertToType(napi_env env, napi_value value, std::shared_ptr "No initializer found that matches constructor invocation."); return nullptr; } else if (candidates.size() > 1) { + auto scoreCandidate = [&](const Candidate& candidate) -> int { + int score = candidate.bonus; + const char* selectorCStr = sel_getName(candidate.member->methodOrGetter.selector); + std::string selectorName = selectorCStr != nullptr ? selectorCStr : ""; + std::string selectorNameLower = selectorName; + std::transform(selectorNameLower.begin(), selectorNameLower.end(), selectorNameLower.begin(), + [](unsigned char ch) { return (char)std::tolower(ch); }); + + for (size_t i = 0; i < candidate.args.size(); ++i) { + napi_valuetype jsType = napi_undefined; + napi_typeof(env, candidate.args[i], &jsType); + auto kind = candidate.cif->argTypes[i]->kind; + + if (jsType == napi_object) { + bool isArray = false; + napi_is_array(env, candidate.args[i], &isArray); + if (isArray) { + if (selectorNameLower.find("array") != std::string::npos) { + score += 8; + } + if (selectorNameLower.find("url") != std::string::npos || + selectorNameLower.find("file") != std::string::npos || + selectorNameLower.find("coder") != std::string::npos) { + score -= 2; + } + } + } + + if ((kind == mdTypeNSStringObject || kind == mdTypeNSMutableStringObject) && + jsType == napi_string) { + score += 4; + } else if ((kind == mdTypeStruct) && jsType == napi_object) { + score += 4; + } else if ((kind == mdTypeBool && jsType == napi_boolean) || + ((kind == mdTypeSInt || kind == mdTypeUInt || kind == mdTypeSLong || + kind == mdTypeULong || kind == mdTypeSInt64 || kind == mdTypeUInt64 || + kind == mdTypeFloat || kind == mdTypeDouble) && + (jsType == napi_number || jsType == napi_bigint))) { + score += 3; + } else if ((kind == mdTypeClass || kind == mdTypeClassObject || + kind == mdTypeProtocolObject) && + jsType == napi_function) { + score += 3; + } else if ((kind == mdTypeAnyObject || kind == mdTypeInstanceObject) && + (jsType == napi_object || jsType == napi_function)) { + score += 2; + } else if (kind == mdTypeString && jsType == napi_string) { + score += 2; + } else { + score += 1; + } + } + return score; + }; + + int bestScore = std::numeric_limits::min(); + Candidate* bestCandidate = nullptr; + bool tie = false; + for (auto& candidate : candidates) { + int score = scoreCandidate(candidate); + if (score > bestScore) { + bestScore = score; + bestCandidate = &candidate; + tie = false; + } else if (score == bestScore) { + tie = true; + } + } + + if (bestCandidate != nullptr && !tie) { + if (selectedArgs != nullptr) { + *selectedArgs = bestCandidate->args; + } + bestCandidate->member->cif = bestCandidate->cif; + return bestCandidate->member; + } + // Prefer "init" if no arguments if (argc == 0) { - for (auto* candidate : candidates) { - const char* selectorName = sel_getName(candidate->methodOrGetter.selector); + for (const auto& candidate : candidates) { + const char* selectorName = sel_getName(candidate.member->methodOrGetter.selector); if (strcmp(selectorName, "init") == 0) { - return candidate; + if (selectedArgs != nullptr) { + selectedArgs->clear(); + } + candidate.member->cif = candidate.cif; + return candidate.member; } } } // If multiple candidates, throw an error with details std::string errorMsg = "More than one initializer found that matches constructor invocation:"; - for (const auto* candidate : candidates) { + std::unordered_set uniqueMembers; + for (const auto& candidate : candidates) { + if (!uniqueMembers.insert(candidate.member).second) { + continue; + } errorMsg += " "; - errorMsg += sel_getName(candidate->methodOrGetter.selector); + errorMsg += sel_getName(candidate.member->methodOrGetter.selector); } napi_throw_error(env, "NativeScriptException", errorMsg.c_str()); return nullptr; } - return candidates[0]; + if (selectedArgs != nullptr) { + *selectedArgs = candidates[0].args; + } + candidates[0].member->cif = candidates[0].cif; + return candidates[0].member; } -inline id assertSelf(napi_env env, napi_value jsThis) { - id self; - napi_unwrap(env, jsThis, (void**)&self); +inline id assertSelf(napi_env env, napi_value jsThis, ObjCClassMember* method = nullptr) { + id self = nil; + napi_status unwrapStatus = napi_unwrap(env, jsThis, (void**)&self); + + if (unwrapStatus == napi_ok && self != nil) { + return self; + } + + bool shouldUseClassFallback = false; + if (method != nullptr && method->cls != nullptr && method->cls->nativeClass != nil) { + if (method->classMethod) { + shouldUseClassFallback = true; + } else { + napi_valuetype jsType = napi_undefined; + if (napi_typeof(env, jsThis, &jsType) == napi_ok && jsType == napi_function) { + shouldUseClassFallback = true; + } + } + } + + if (shouldUseClassFallback) { + return (id)method->cls->nativeClass; + } + + napi_throw_error(env, "NativeScriptException", + "There was no native counterpart to the JavaScript object. Native API was " + "called with a likely plain object."); + return nullptr; +} + +ObjCClass* resolveInitMetadataClass(napi_env env, napi_value jsThis, ObjCClassMember* method, + Class nativeClass) { + ObjCBridgeState* state = ObjCBridgeState::InstanceData(env); + if (state == nullptr) { + return method != nullptr ? method->cls : nullptr; + } + + auto resolveFromClass = [&](Class cls) -> ObjCClass* { + if (cls == nil) { + return nullptr; + } + + auto bridgedIt = state->classesByPointer.find(cls); + if (bridgedIt != state->classesByPointer.end() && bridgedIt->second != nullptr) { + ObjCClass* bridgedClass = bridgedIt->second; + if (bridgedClass->metadataOffset != MD_SECTION_OFFSET_NULL) { + return bridgedClass; + } + if (bridgedClass->superclass != nullptr) { + return bridgedClass->superclass; + } + } + + auto mdClsIt = state->mdClassesByPointer.find(cls); + if (mdClsIt != state->mdClassesByPointer.end()) { + return state->getClass(env, mdClsIt->second); + } - if (self == nil) { - napi_throw_error(env, "NativeScriptException", - "There was no native counterpart to the JavaScript object. Native API was " - "called with a likely plain object."); return nullptr; + }; + + if (jsThis != nullptr) { + napi_value constructor = nullptr; + if (napi_get_named_property(env, jsThis, "constructor", &constructor) == napi_ok && + constructor != nullptr) { + Class constructorClass = nil; + if (napi_unwrap(env, constructor, (void**)&constructorClass) == napi_ok) { + ObjCClass* resolved = resolveFromClass(constructorClass); + if (resolved != nullptr) { + return resolved; + } + } + } + } + + if (nativeClass != nil) { + for (Class current = nativeClass; current != nil; current = class_getSuperclass(current)) { + ObjCClass* resolved = resolveFromClass(current); + if (resolved != nullptr) { + return resolved; + } + } + } + + if (method != nullptr && method->cls != nullptr) { + return method->cls; } - return self; + return nullptr; } +class RoundTripCacheFrameGuard { + public: + RoundTripCacheFrameGuard(napi_env env, ObjCBridgeState* bridgeState) + : env_(env), bridgeState_(bridgeState) { + if (bridgeState_ != nullptr) { + bridgeState_->beginRoundTripCacheFrame(env_); + } + } + + ~RoundTripCacheFrameGuard() { + if (bridgeState_ != nullptr) { + bridgeState_->endRoundTripCacheFrame(env_); + } + } + + private: + napi_env env_; + ObjCBridgeState* bridgeState_; +}; + +namespace { + +inline size_t alignUpSize(size_t value, size_t alignment) { + if (alignment == 0) { + return value; + } + return ((value + alignment - 1) / alignment) * alignment; +} + +size_t getCifArgumentStorageSize(Cif* cif, unsigned int argumentIndex, + unsigned int implicitArgumentCount) { + if (cif == nullptr || cif->cif.arg_types == nullptr) { + return sizeof(void*); + } + + const unsigned int ffiIndex = argumentIndex + implicitArgumentCount; + if (ffiIndex >= cif->cif.nargs) { + return sizeof(void*); + } + + ffi_type* ffiArgType = cif->cif.arg_types[ffiIndex]; + size_t storageSize = ffiArgType != nullptr ? ffiArgType->size : 0; + if (storageSize == 0) { + storageSize = sizeof(void*); + } + + return storageSize; +} + +size_t getCifArgumentStorageAlign(Cif* cif, unsigned int argumentIndex, + unsigned int implicitArgumentCount) { + if (cif == nullptr || cif->cif.arg_types == nullptr) { + return alignof(void*); + } + + const unsigned int ffiIndex = argumentIndex + implicitArgumentCount; + if (ffiIndex >= cif->cif.nargs) { + return alignof(void*); + } + + ffi_type* ffiArgType = cif->cif.arg_types[ffiIndex]; + size_t alignment = ffiArgType != nullptr ? ffiArgType->alignment : 0; + if (alignment == 0) { + alignment = alignof(void*); + } + + return alignment; +} + +class CifArgumentStorage { + public: + CifArgumentStorage(Cif* cif, unsigned int implicitArgumentCount) { + if (cif == nullptr || cif->argc == 0) { + return; + } + + buffers_.resize(cif->argc, nullptr); + + size_t totalSize = 0; + for (unsigned int i = 0; i < cif->argc; i++) { + const size_t storageAlign = getCifArgumentStorageAlign(cif, i, implicitArgumentCount); + const size_t storageSize = getCifArgumentStorageSize(cif, i, implicitArgumentCount); + totalSize = alignUpSize(totalSize, storageAlign); + totalSize += storageSize; + } + + if (totalSize == 0) { + totalSize = sizeof(void*); + } + + if (totalSize <= kInlineSize) { + storageBase_ = inlineBuffer_; + } else { + storageBase_ = malloc(totalSize); + } + + if (storageBase_ == nullptr) { + valid_ = false; + return; + } + + memset(storageBase_, 0, totalSize); + + size_t offset = 0; + for (unsigned int i = 0; i < cif->argc; i++) { + const size_t storageAlign = getCifArgumentStorageAlign(cif, i, implicitArgumentCount); + const size_t storageSize = getCifArgumentStorageSize(cif, i, implicitArgumentCount); + offset = alignUpSize(offset, storageAlign); + buffers_[i] = static_cast(static_cast(storageBase_) + offset); + offset += storageSize; + } + } + + ~CifArgumentStorage() { + if (storageBase_ != nullptr && storageBase_ != inlineBuffer_) { + free(storageBase_); + } + } + + bool valid() const { return valid_; } + + void* at(unsigned int index) const { + if (index >= buffers_.size()) { + return nullptr; + } + + return buffers_[index]; + } + + private: + static constexpr size_t kInlineSize = 256; + alignas(max_align_t) unsigned char inlineBuffer_[kInlineSize]; + void* storageBase_ = nullptr; + bool valid_ = true; + std::vector buffers_; +}; + +class CifReturnStorage { + public: + explicit CifReturnStorage(Cif* cif) { + size_ = 0; + if (cif != nullptr) { + size_ = cif->rvalueLength; + if (size_ == 0 && cif->cif.rtype != nullptr) { + size_ = cif->cif.rtype->size; + } + } + if (size_ == 0) { + size_ = sizeof(void*); + } + + if (size_ <= kInlineSize) { + data_ = inlineBuffer_; + memset(data_, 0, size_); + return; + } + + data_ = malloc(size_); + if (data_ != nullptr) { + memset(data_, 0, size_); + } + } + + ~CifReturnStorage() { + if (data_ != nullptr && data_ != inlineBuffer_) { + free(data_); + } + } + + bool valid() const { return data_ != nullptr; } + + void* get() const { return data_; } + + private: + static constexpr size_t kInlineSize = 32; + alignas(max_align_t) unsigned char inlineBuffer_[kInlineSize]; + void* data_ = nullptr; + size_t size_ = 0; +}; + +} // namespace + napi_value ObjCClassMember::jsCallInit(napi_env env, napi_callback_info cbinfo) { napi_value jsThis; ObjCClassMember* method; @@ -363,24 +1182,43 @@ inline id assertSelf(napi_env env, napi_value jsThis) { size_t argc = 0; napi_get_cb_info(env, cbinfo, &argc, nullptr, &jsThis, (void**)&method); - id self = assertSelf(env, jsThis); + id self = assertSelf(env, jsThis, method); if (self == nullptr) { return nullptr; } + RoundTripCacheFrameGuard roundTripCacheFrame(env, method->bridgeState); + SEL sel = method->methodOrGetter.selector; + Class nativeClass = [self class]; + std::vector resolvedInitArgs; if (sel == @selector(init) && argc > 0) { - napi_value argv[argc]; - napi_get_cb_info(env, cbinfo, &argc, argv, &jsThis, nullptr); - Class nativeClass = [self class]; - ObjCBridgeState* state = ObjCBridgeState::InstanceData(env); - ObjCClass* cls = state->classesByPointer[nativeClass]; - // NSLog(@"find init for class: %@, cls: %p", nativeClass, cls); - ObjCClassMember* newMethod = findInitializerForArgs(env, &cls->members, argc, argv); - // NSLog(@"new init: %p", newMethod); + std::vector callArgs(argc); + napi_status cbStatus = napi_get_cb_info(env, cbinfo, &argc, callArgs.data(), &jsThis, nullptr); + if (cbStatus != napi_ok) { + napi_throw_error(env, "NativeScriptException", + "Unable to read constructor arguments for initializer resolution."); + return nullptr; + } + callArgs.resize(argc); + ObjCClass* cls = resolveInitMetadataClass(env, jsThis, method, nativeClass); + if (cls == nullptr) { + napi_throw_error(env, "NativeScriptException", + "Unable to resolve Objective-C class metadata for initializer invocation."); + return nullptr; + } + + ObjCClassMember* newMethod = findInitializerForArgs(env, &cls->members, nativeClass, argc, + callArgs.data(), &resolvedInitArgs); if (newMethod != nullptr) { method = newMethod; + } else { + bool pendingException = false; + napi_is_exception_pending(env, &pendingException); + if (pendingException) { + return nullptr; + } } } @@ -390,8 +1228,76 @@ inline id assertSelf(napi_env env, napi_value jsThis) { method->bridgeState->getMethodCif(env, method->methodOrGetter.signatureOffset); } - argc = cif->argc; - napi_get_cb_info(env, cbinfo, &argc, cif->argv, &jsThis, nullptr); + if (!resolvedInitArgs.empty()) { + argc = resolvedInitArgs.size(); + if (argc != cif->argc) { + napi_throw_error(env, "NativeScriptException", + "Initializer resolution produced invalid argument count."); + return nullptr; + } + for (size_t i = 0; i < argc; i++) { + cif->argv[i] = resolvedInitArgs[i]; + } + } else { + argc = cif->argc; + napi_get_cb_info(env, cbinfo, &argc, cif->argv, &jsThis, nullptr); + } + + id rvalue = nil; + bool retainedReceiver = false; + const bool receiverIsClass = object_isClass(self); + if (!receiverIsClass) { + // init can return a different object and release the original receiver. + // Keep the original receiver alive while this JS wrapper still references it. + [self retain]; + retainedReceiver = true; + } + + bool didDirectInvoke = false; + if (!tryObjCNapiDispatch(env, cif, self, receiverIsClass, method->methodOrGetter.selector, + &method->methodOrGetter, method->methodOrGetter.dispatchFlags, cif->argv, + &rvalue, &didDirectInvoke)) { + if (retainedReceiver) { + [self release]; + } + return nullptr; + } + + if (didDirectInvoke) { + if (rvalue == nil) { + if (retainedReceiver) { + [self release]; + } + napi_value result; + napi_get_null(env, &result); + return result; + } + + napi_value constructor = jsThis; + if (!receiverIsClass) { + napi_get_named_property(env, jsThis, "constructor", &constructor); + } + + napi_value result = method->bridgeState->getObject(env, rvalue, constructor, kUnownedObject); + + if (rvalue != self) { + [rvalue release]; + } else if (retainedReceiver) { + [self release]; + } + + return result; + } + + CifArgumentStorage argStorage(cif, 2); + if (!argStorage.valid()) { + if (retainedReceiver) { + [self release]; + } + napi_throw_error(env, "NativeScriptException", + "Unable to allocate argument storage for Objective-C call."); + return nullptr; + } void* avalues[cif->cif.nargs]; @@ -404,37 +1310,47 @@ inline id assertSelf(napi_env env, napi_value jsThis) { if (cif->argc > 0) { for (unsigned int i = 0; i < cif->argc; i++) { shouldFree[i] = false; - avalues[i + 2] = cif->avalues[i]; + avalues[i + 2] = argStorage.at(i); cif->argTypes[i]->toNative(env, cif->argv[i], avalues[i + 2], &shouldFree[i], &shouldFreeAny); } } - id rvalue; - - if (!objcNativeCall(env, cif, self, avalues, &rvalue)) { + if (!objcNativeCall(env, cif, self, receiverIsClass, &method->methodOrGetter, + method->methodOrGetter.dispatchFlags, avalues, &rvalue)) { + if (retainedReceiver) { + [self release]; + } return nullptr; } - for (unsigned int i = 0; i < cif->argc; i++) { - if (shouldFree[i]) { - cif->argTypes[i]->free(env, *((void**)avalues[i + 2])); + if (shouldFreeAny) { + for (unsigned int i = 0; i < cif->argc; i++) { + if (shouldFree[i]) { + cif->argTypes[i]->free(env, *((void**)avalues[i + 2])); + } } } if (rvalue == nil) { + if (retainedReceiver) { + [self release]; + } napi_value result; napi_get_null(env, &result); return result; } napi_value constructor = jsThis; - if (!method->classMethod) napi_get_named_property(env, jsThis, "constructor", &constructor); + if (!receiverIsClass) { + napi_get_named_property(env, jsThis, "constructor", &constructor); + } napi_value result = method->bridgeState->getObject(env, rvalue, constructor, kUnownedObject); if (rvalue != self) { - [self retain]; [rvalue release]; + } else if (retainedReceiver) { + [self release]; } return result; @@ -444,61 +1360,337 @@ inline id assertSelf(napi_env env, napi_value jsThis) { napi_value jsThis; ObjCClassMember* method; - napi_get_cb_info(env, cbinfo, nullptr, nullptr, &jsThis, (void**)&method); + size_t actualArgc = 16; + napi_value stackArgs[16]; + napi_get_cb_info(env, cbinfo, &actualArgc, stackArgs, &jsThis, (void**)&method); - id self = assertSelf(env, jsThis); + id self = assertSelf(env, jsThis, method); if (self == nullptr) { return nullptr; } - Cif* cif = method->cif; + RoundTripCacheFrameGuard roundTripCacheFrame(env, method->bridgeState); + + const bool receiverIsClass = object_isClass(self); + Class receiverClass = receiverIsClass ? (Class)self : [self class]; + auto resolveDescriptorCif = [&](MethodDescriptor* descriptor, Cif** cacheSlot) -> Cif* { + if (descriptor == nullptr || cacheSlot == nullptr) { + return nullptr; + } + + Cif* cached = *cacheSlot; + if (cached != nullptr) { + return cached; + } + + Method runtimeMethod = receiverIsClass + ? class_getClassMethod(receiverClass, descriptor->selector) + : class_getInstanceMethod(receiverClass, descriptor->selector); + Cif* resolved = nullptr; + if (runtimeMethod != nullptr) { + resolved = method->bridgeState->getMethodCif(env, runtimeMethod); + } + if (resolved == nullptr) { + resolved = method->bridgeState->getMethodCif(env, descriptor->signatureOffset); + } + + *cacheSlot = resolved; + return resolved; + }; + + const napi_value* callArgs = stackArgs; + std::vector dynamicArgs; + if (actualArgc > 16) { + dynamicArgs.resize(actualArgc); + size_t argcRetry = actualArgc; + napi_get_cb_info(env, cbinfo, &argcRetry, dynamicArgs.data(), &jsThis, (void**)&method); + dynamicArgs.resize(argcRetry); + actualArgc = argcRetry; + callArgs = dynamicArgs.data(); + } else if (!method->overloads.empty()) { + dynamicArgs.assign(stackArgs, stackArgs + actualArgc); + callArgs = dynamicArgs.data(); + } + + MethodDescriptor* selectedMethod = &method->methodOrGetter; + Cif* selectedCif = method->cif; + if (selectedCif == nullptr) { + selectedCif = method->bridgeState->getMethodCif(env, method->methodOrGetter.signatureOffset); + method->cif = selectedCif; + } + + if (!method->overloads.empty()) { + struct Candidate { + MethodDescriptor* descriptor; + Cif* cif; + int score; + }; + + std::vector candidates; + auto tryAddCandidate = [&](MethodDescriptor* descriptor, Cif* cif) { + if (descriptor == nullptr || cif == nullptr || cif->argc != actualArgc) { + return; + } + + int score = 0; + for (size_t i = 0; i < actualArgc; i++) { + if (!canConvertToType(env, callArgs[i], cif->argTypes[i])) { + return; + } + napi_valuetype jsType = napi_undefined; + napi_typeof(env, callArgs[i], &jsType); + switch (cif->argTypes[i]->kind) { + case mdTypeBool: + if (jsType == napi_boolean) score += 2; + break; + case mdTypeSInt: + case mdTypeUInt: + case mdTypeSLong: + case mdTypeULong: + case mdTypeSInt64: + case mdTypeUInt64: + case mdTypeFloat: + case mdTypeDouble: + if (jsType == napi_number || jsType == napi_bigint) score += 2; + break; + case mdTypeString: + case mdTypeNSStringObject: + case mdTypeNSMutableStringObject: + if (jsType == napi_string) score += 2; + break; + default: + score += 1; + break; + } + } + + candidates.push_back(Candidate{descriptor, cif, score}); + }; + + tryAddCandidate(&method->methodOrGetter, selectedCif); + for (auto& overload : method->overloads) { + Cif* overloadCif = resolveDescriptorCif(&overload.method, &overload.cif); + tryAddCandidate(&overload.method, overloadCif); + } + + if (!candidates.empty()) { + Candidate* best = &candidates[0]; + for (auto& candidate : candidates) { + if (candidate.score > best->score) { + best = &candidate; + } + } + selectedMethod = best->descriptor; + selectedCif = best->cif; + } + } + + Cif* cif = selectedCif; if (cif == nullptr) { - cif = method->cif = - method->bridgeState->getMethodCif(env, method->methodOrGetter.signatureOffset); + napi_throw_error(env, "NativeScriptException", "Unable to resolve native call signature."); + return nullptr; } - size_t argc = cif->argc; - napi_get_cb_info(env, cbinfo, &argc, cif->argv, &jsThis, nullptr); + CifReturnStorage rvalueStorage(cif); + if (!rvalueStorage.valid()) { + napi_throw_error(env, "NativeScriptException", + "Unable to allocate return value storage for Objective-C call."); + return nullptr; + } - void* avalues[cif->cif.nargs]; - void* rvalue = cif->rvalue; + SEL selectedSelector = selectedMethod->selector; + const char* selectedSelectorName = sel_getName(selectedSelector); + const bool isNSErrorOutMethod = isNSErrorOutMethodSignature(selectedSelector, cif); + if (!cif->isVariadic && isNSErrorOutMethod) { + if (actualArgc > cif->argc || actualArgc + 1 < cif->argc) { + throwArgumentsCountError(env, actualArgc, cif->argc); + return nullptr; + } + } + + void* rvalue = rvalueStorage.get(); + const bool hasImplicitNSErrorOutArg = + isNSErrorOutMethod && !cif->isVariadic && actualArgc + 1 == cif->argc; + const napi_value* invocationArgs = callArgs; + std::vector paddedArgs; + if (actualArgc != cif->argc) { + napi_value jsUndefined = nullptr; + napi_get_undefined(env, &jsUndefined); + paddedArgs.assign(cif->argc, jsUndefined); + const size_t copyArgc = std::min(actualArgc, static_cast(cif->argc)); + if (copyArgc > 0) { + memcpy(paddedArgs.data(), callArgs, copyArgc * sizeof(napi_value)); + } + invocationArgs = paddedArgs.data(); + } + + auto blockEncodingForSelector = [](const char* selectorName, + unsigned int argIndex) -> const char* { + if (selectorName == nullptr || argIndex != 0) { + return nullptr; + } + + if (strcmp(selectorName, "methodWithSimpleBlock:") == 0 || + strcmp(selectorName, "methodRetainingBlock:") == 0 || + strcmp(selectorName, "methodWithBlock:") == 0) { + return "v"; + } + if (strcmp(selectorName, "methodWithComplexBlock:") == 0) { + return "@i@:@{TNSOStruct=iii}"; + } + + return nullptr; + }; + + auto toJSResult = [&](void* nativeResult) -> napi_value { + if (selectedSelectorName != nullptr && strcmp(selectedSelectorName, "class") == 0) { + if (!receiverIsClass) { + napi_value constructor = jsThis; + napi_get_named_property(env, jsThis, "constructor", &constructor); + return constructor; + } + + id classObject = self; + return method->bridgeState->getObject(env, classObject, kUnownedObject, 0, nullptr); + } + + if (cif->returnType->kind == mdTypeInstanceObject) { + napi_value constructor = jsThis; + if (!receiverIsClass) { + napi_get_named_property(env, jsThis, "constructor", &constructor); + } + id obj = *((id*)nativeResult); + return method->bridgeState->getObject(env, obj, constructor, + method->returnOwned ? kOwnedObject : kUnownedObject); + } + + if (cif->returnType->kind == mdTypeAnyObject) { + id obj = *((id*)nativeResult); + if (receiverIsClass && obj != nil) { + Class receiverClass = (Class)self; + if (receiverClass == [NSString class] || receiverClass == [NSMutableString class]) { + if (selectedSelectorName != nullptr && + (strcmp(selectedSelectorName, "string") == 0 || + strcmp(selectedSelectorName, "stringWithString:") == 0 || + strcmp(selectedSelectorName, "stringWithCapacity:") == 0)) { + return method->bridgeState->getObject(env, obj, jsThis, kUnownedObject); + } + } + } + } + + return cif->returnType->toJS(env, nativeResult, method->returnOwned ? kReturnOwned : 0); + }; + + bool usesBlockFallback = false; + if (cif->argc > 0) { + for (unsigned int i = 0; i < cif->argc; i++) { + const char* blockEncoding = blockEncodingForSelector(selectedSelectorName, i); + if (blockEncoding != nullptr && cif->argTypes[i]->kind == mdTypeAnyObject) { + napi_valuetype jsArgType = napi_undefined; + if (napi_typeof(env, invocationArgs[i], &jsArgType) == napi_ok && + jsArgType == napi_function) { + usesBlockFallback = true; + break; + } + } + } + } + + if (!hasImplicitNSErrorOutArg && !usesBlockFallback) { + bool didDirectInvoke = false; + if (!tryObjCNapiDispatch(env, cif, self, receiverIsClass, selectedSelector, selectedMethod, + selectedMethod->dispatchFlags, invocationArgs, rvalue, + &didDirectInvoke)) { + return nullptr; + } + + if (didDirectInvoke) { + return toJSResult(rvalue); + } + } + + CifArgumentStorage argStorage(cif, 2); + if (!argStorage.valid()) { + napi_throw_error(env, "NativeScriptException", + "Unable to allocate argument storage for Objective-C call."); + return nullptr; + } + + void* avalues[cif->cif.nargs]; avalues[0] = (void*)&self; - avalues[1] = (void*)&method->methodOrGetter.selector; + avalues[1] = (void*)&selectedSelector; bool shouldFreeAny = false; bool shouldFree[cif->argc]; + std::vector fallbackBlocksToRelease; + NSError* implicitNSError = nil; if (cif->argc > 0) { for (unsigned int i = 0; i < cif->argc; i++) { shouldFree[i] = false; - avalues[i + 2] = cif->avalues[i]; - cif->argTypes[i]->toNative(env, cif->argv[i], avalues[i + 2], &shouldFree[i], &shouldFreeAny); + avalues[i + 2] = argStorage.at(i); + const char* blockEncoding = blockEncodingForSelector(selectedSelectorName, i); + + if (hasImplicitNSErrorOutArg && i == cif->argc - 1) { + *((NSError***)avalues[i + 2]) = &implicitNSError; + continue; + } + + bool convertedViaBlockFallback = false; + if (blockEncoding != nullptr && cif->argTypes[i]->kind == mdTypeAnyObject) { + napi_valuetype jsArgType = napi_undefined; + if (napi_typeof(env, invocationArgs[i], &jsArgType) == napi_ok && + jsArgType == napi_function) { + auto closure = new Closure(std::string(blockEncoding), true); + closure->env = env; + id block = registerBlock(env, closure, invocationArgs[i]); + *((void**)avalues[i + 2]) = (void*)block; + fallbackBlocksToRelease.push_back(block); + convertedViaBlockFallback = true; + } + } + + if (!convertedViaBlockFallback) { + cif->argTypes[i]->toNative(env, invocationArgs[i], avalues[i + 2], &shouldFree[i], + &shouldFreeAny); + } } } // NSLog(@"objcNativeCall: %p, %@", self, NSStringFromSelector(method->methodOrGetter.selector)); - if (!objcNativeCall(env, cif, self, avalues, rvalue)) { + if (!objcNativeCall(env, cif, self, receiverIsClass, selectedMethod, + selectedMethod->dispatchFlags, avalues, rvalue)) { + for (id block : fallbackBlocksToRelease) { + [block release]; + } return nullptr; } - for (unsigned int i = 0; i < cif->argc; i++) { - if (shouldFree[i]) { - cif->argTypes[i]->free(env, *((void**)avalues[i + 2])); + for (id block : fallbackBlocksToRelease) { + [block release]; + } + + if (shouldFreeAny) { + for (unsigned int i = 0; i < cif->argc; i++) { + if (shouldFree[i]) { + cif->argTypes[i]->free(env, *((void**)avalues[i + 2])); + } } } - if (cif->returnType->kind == mdTypeInstanceObject) { - napi_value constructor = jsThis; - if (!method->classMethod) napi_get_named_property(env, jsThis, "constructor", &constructor); - id obj = *((id*)rvalue); - return method->bridgeState->getObject(env, obj, constructor, - method->returnOwned ? kOwnedObject : kUnownedObject); + if (hasImplicitNSErrorOutArg && implicitNSError != nil) { + const char* errorMessage = [[implicitNSError description] UTF8String]; + NativeScriptException nativeScriptException(errorMessage != nullptr ? errorMessage + : "Unknown NSError"); + nativeScriptException.ReThrowToJS(env); + return nullptr; } - return cif->returnType->toJS(env, rvalue, method->returnOwned ? kReturnOwned : 0); + return toJSResult(rvalue); } napi_value ObjCClassMember::jsGetter(napi_env env, napi_callback_info cbinfo) { @@ -507,40 +1699,71 @@ inline id assertSelf(napi_env env, napi_value jsThis) { napi_get_cb_info(env, cbinfo, nullptr, nullptr, &jsThis, (void**)&method); - id self = assertSelf(env, jsThis); + id self = assertSelf(env, jsThis, method); if (self == nullptr) { return nullptr; } + const bool receiverIsClass = object_isClass(self); + Cif* cif = method->cif; if (cif == nullptr) { cif = method->cif = method->bridgeState->getMethodCif(env, method->methodOrGetter.signatureOffset); } - void* avalues[2] = {&self, &method->methodOrGetter.selector}; - void* rvalue = cif->rvalue; + CifReturnStorage rvalueStorage(cif); + if (!rvalueStorage.valid()) { + napi_throw_error(env, "NativeScriptException", + "Unable to allocate return value storage for Objective-C getter call."); + return nullptr; + } - // NSLog(@"objcNativeCall: %p, %@", self, NSStringFromSelector(method->methodOrGetter.selector)); + void* avalues[2] = {&self, &method->methodOrGetter.selector}; + void* rvalue = rvalueStorage.get(); - if (!objcNativeCall(env, cif, self, avalues, rvalue)) { + bool didDirectInvoke = false; + if (!tryObjCNapiDispatch(env, cif, self, receiverIsClass, method->methodOrGetter.selector, + &method->methodOrGetter, method->methodOrGetter.dispatchFlags, nullptr, + rvalue, &didDirectInvoke)) { return nullptr; } + if (!didDirectInvoke) { + // NSLog(@"objcNativeCall: %p, %@", self, + // NSStringFromSelector(method->methodOrGetter.selector)); + + if (!objcNativeCall(env, cif, self, receiverIsClass, &method->methodOrGetter, + method->methodOrGetter.dispatchFlags, avalues, rvalue)) { + return nullptr; + } + } + + const char* selectorName = sel_getName(method->methodOrGetter.selector); + if (strcmp(selectorName, "class") == 0) { + id classObject = receiverIsClass ? self : (id)object_getClass(self); + return method->bridgeState->getObject(env, classObject, kUnownedObject, 0, nullptr); + } + if (cif->returnType->kind == mdTypeInstanceObject) { napi_value constructor = jsThis; - if (!method->classMethod) { + if (!receiverIsClass) { napi_get_named_property(env, jsThis, "constructor", &constructor); } - - return method->bridgeState->getObject(env, *((id*)rvalue), constructor, + id obj = *((id*)rvalue); + return method->bridgeState->getObject(env, obj, constructor, method->returnOwned ? kOwnedObject : kUnownedObject); } return cif->returnType->toJS(env, rvalue, 0); } +napi_value ObjCClassMember::jsReadOnlySetter(napi_env env, napi_callback_info cbinfo) { + napi_throw_error(env, nullptr, "Attempted to assign to readonly property."); + return nullptr; +} + napi_value ObjCClassMember::jsSetter(napi_env env, napi_callback_info cbinfo) { napi_value jsThis, argv; size_t argc = 1; @@ -548,25 +1771,52 @@ inline id assertSelf(napi_env env, napi_value jsThis) { napi_get_cb_info(env, cbinfo, &argc, &argv, &jsThis, (void**)&method); - id self = assertSelf(env, jsThis); + id self = assertSelf(env, jsThis, method); if (self == nullptr) { return nullptr; } + const bool receiverIsClass = object_isClass(self); + + RoundTripCacheFrameGuard roundTripCacheFrame(env, method->bridgeState); + Cif* cif = method->setterCif; if (cif == nullptr) { cif = method->setterCif = method->bridgeState->getMethodCif(env, method->setter.signatureOffset); } - void* avalues[3] = {&self, &method->setter.selector, cif->avalues[0]}; + if (cif->argc > 0) { + cif->argv[0] = argv; + } + + bool didDirectInvoke = false; + if (!tryObjCNapiDispatch(env, cif, self, receiverIsClass, method->setter.selector, + &method->setter, method->setter.dispatchFlags, cif->argv, nullptr, + &didDirectInvoke)) { + return nullptr; + } + + if (didDirectInvoke) { + return nullptr; + } + + CifArgumentStorage argStorage(cif, 2); + if (!argStorage.valid()) { + napi_throw_error(env, "NativeScriptException", + "Unable to allocate argument storage for Objective-C setter call."); + return nullptr; + } + + void* avalues[3] = {&self, &method->setter.selector, argStorage.at(0)}; void* rvalue = nullptr; bool shouldFree = false; cif->argTypes[0]->toNative(env, argv, avalues[2], &shouldFree, &shouldFree); - if (!objcNativeCall(env, cif, self, avalues, rvalue)) { + if (!objcNativeCall(env, cif, self, receiverIsClass, &method->setter, + method->setter.dispatchFlags, avalues, rvalue)) { return nullptr; } diff --git a/NativeScript/ffi/Closure.h b/NativeScript/ffi/Closure.h index dc59fdaa..572e279e 100644 --- a/NativeScript/ffi/Closure.h +++ b/NativeScript/ffi/Closure.h @@ -1,6 +1,9 @@ #ifndef CLOSURE_H #define CLOSURE_H +#include + +#include #include #include @@ -17,12 +20,14 @@ class Closure { static void callBlockFromMainThread(napi_env env, napi_value js_cb, void* context, void* data); - Closure(std::string typeEncoding, bool isBlock); + Closure(std::string typeEncoding, bool isBlock, bool isMethod = false); Closure(MDMetadataReader* reader, MDSectionOffset offset, bool isBlock = false, std::string* encoding = nullptr, bool isMethod = false, bool isGetter = false, bool isSetter = false); ~Closure(); + void retain(); + void release(); napi_env env; napi_ref thisConstructor; @@ -30,9 +35,12 @@ class Closure { bool isGetter = false; bool isSetter = false; std::string propertyName; + SEL selector = nullptr; napi_threadsafe_function tsfn; std::thread::id jsThreadId = std::this_thread::get_id(); + CFRunLoopRef jsRunLoop = CFRunLoopGetCurrent(); + std::atomic retainCount{1}; ffi_cif cif; ffi_closure* closure; diff --git a/NativeScript/ffi/Closure.mm b/NativeScript/ffi/Closure.mm index 99294441..2c57aef5 100644 --- a/NativeScript/ffi/Closure.mm +++ b/NativeScript/ffi/Closure.mm @@ -14,13 +14,87 @@ #include "node_api_util.h" #include "objc/message.h" +#include #include +#include #include #include +#include #include +#include namespace nativescript { +namespace { + +inline void deleteClosureOnOwningThread(Closure* closure) { + if (closure == nullptr) { + return; + } + +#ifdef ENABLE_JS_RUNTIME + if (std::this_thread::get_id() != closure->jsThreadId) { + CFRunLoopRef runloop = closure->jsRunLoop; + if (runloop == nullptr) { + runloop = CFRunLoopGetMain(); + } + + if (runloop != nullptr) { + CFRunLoopPerformBlock(runloop, kCFRunLoopCommonModes, ^{ + delete closure; + }); + CFRunLoopWakeUp(runloop); + return; + } + } +#endif // ENABLE_JS_RUNTIME + + delete closure; +} + +} // namespace + +inline bool selectorEndsWithErrorParam(SEL selector) { + if (selector == nullptr) { + return false; + } + + const char* selectorName = sel_getName(selector); + if (selectorName == nullptr) { + return false; + } + + size_t selectorLength = strlen(selectorName); + const char* suffix = "error:"; + size_t suffixLength = strlen(suffix); + if (selectorLength < suffixLength) { + return false; + } + + return strcmp(selectorName + selectorLength - suffixLength, suffix) == 0; +} + +inline bool isNSErrorMethodCallback(Closure* closure, ffi_cif* cif) { + if (closure == nullptr || cif == nullptr || cif->nargs < 3 || closure->returnType == nullptr) { + return false; + } + + if (closure->returnType->kind != mdTypeBool) { + return false; + } + + if (closure->argTypes.size() != cif->nargs) { + return false; + } + + auto lastArgType = closure->argTypes[cif->nargs - 1]; + if (lastArgType == nullptr || lastArgType->kind != mdTypePointer) { + return false; + } + + return selectorEndsWithErrorParam(closure->selector); +} + inline void JSCallbackInner(Closure* closure, napi_value func, napi_value thisArg, napi_value* argv, size_t argc, bool* done, void* ret) { napi_env env = closure->env; @@ -31,7 +105,9 @@ inline void JSCallbackInner(Closure* closure, napi_value func, napi_value thisAr napi_status status = napi_call_function(env, thisArg, func, argc, argv, &result); - if (done != NULL) *done = true; + if (done != nullptr) { + *done = true; + } // If the call failed, we need to create an error object and throw it in native // Likely it will circle back to JS. We have try/catch around all native calls from JS, @@ -101,7 +177,104 @@ void JSMethodCallback(ffi_cif* cif, void* ret, void* args[], void* data) { argv[i - 2] = closure->argTypes[i]->toJS(env, args[i], 0); } - JSCallbackInner(closure, func, thisArg, argv, cif->nargs - 2, nullptr, ret); + napi_value result; + napi_get_and_clear_last_exception(env, &result); + + napi_status status = napi_call_function(env, thisArg, func, cif->nargs - 2, argv, &result); + if (status != napi_ok) { + napi_get_and_clear_last_exception(env, &result); + + if (isNSErrorMethodCallback(closure, cif)) { + if (ret != nullptr && cif->rtype != nullptr && cif->rtype->size > 0) { + memset(ret, 0, cif->rtype->size); + } + + void* outArgValue = args[cif->nargs - 1]; + NSError** outError = outArgValue != nullptr ? *((NSError***)outArgValue) : nullptr; + if (outError != nullptr) { + std::string message = "JS error"; + napi_valuetype resultType = napi_undefined; + if (result != nullptr && napi_typeof(env, result, &resultType) == napi_ok) { + if (resultType == napi_object) { + napi_value messageValue = nullptr; + bool hasMessage = false; + if (napi_has_named_property(env, result, "message", &hasMessage) == napi_ok && + hasMessage && + napi_get_named_property(env, result, "message", &messageValue) == napi_ok) { + napi_valuetype messageType = napi_undefined; + if (napi_typeof(env, messageValue, &messageType) == napi_ok && + messageType == napi_string) { + size_t messageLength = 0; + napi_get_value_string_utf8(env, messageValue, nullptr, 0, &messageLength); + std::vector messageBuffer(messageLength + 1); + napi_get_value_string_utf8(env, messageValue, messageBuffer.data(), + messageBuffer.size(), &messageLength); + message.assign(messageBuffer.data(), messageLength); + } + } + } else if (resultType == napi_string) { + size_t messageLength = 0; + napi_get_value_string_utf8(env, result, nullptr, 0, &messageLength); + std::vector messageBuffer(messageLength + 1); + napi_get_value_string_utf8(env, result, messageBuffer.data(), messageBuffer.size(), + &messageLength); + message.assign(messageBuffer.data(), messageLength); + } + } + + NSString* nsMessage = [NSString stringWithUTF8String:message.c_str()]; + NSDictionary* userInfo = nsMessage != nil ? @{NSLocalizedDescriptionKey : nsMessage} : nil; + *outError = [NSError errorWithDomain:@"TNSErrorDomain" code:1 userInfo:userInfo]; + } + + return; + } + + napi_valuetype resultType = napi_undefined; + napi_typeof(env, result, &resultType); + + if (resultType != napi_object) { + napi_value code, msg; + napi_create_string_utf8(env, "NativeScriptException", NAPI_AUTO_LENGTH, &code); + napi_create_string_utf8(env, + "Unable to obtain the error thrown by the JS implemented closure", + NAPI_AUTO_LENGTH, &msg); + napi_create_error(env, code, msg, &result); + } + + NativeScriptException::OnUncaughtError(env, result); + } + + bool shouldFree; + closure->returnType->toNative(env, result, ret, &shouldFree, &shouldFree); +} + +void JSFunctionCallback(ffi_cif* cif, void* ret, void* args[], void* data) { + Closure* closure = (Closure*)data; + napi_env env = closure->env; + +#ifdef ENABLE_JS_RUNTIME + NapiScope scope(env); +#endif + + napi_value func = get_ref_value(env, closure->func); + + napi_valuetype funcType = napi_undefined; + napi_typeof(env, func, &funcType); + if (funcType != napi_function) { + napi_throw_error(env, nullptr, "Function reference is not callable"); + return; + } + + napi_value thisArg; + napi_get_global(env, &thisArg); + + napi_value argv[cif->nargs]; + for (int i = 0; i < cif->nargs; i++) { + argv[i] = closure->argTypes[i]->toJS(env, args[i], 0); + } + + JSCallbackInner(closure, func, thisArg, argv, cif->nargs, nullptr, ret); } struct JSBlockCallContext { @@ -111,6 +284,7 @@ void JSMethodCallback(ffi_cif* cif, void* ret, void* args[], void* data) { std::mutex mutex; std::condition_variable cv; bool done; + bool useCondvar; }; void Closure::callBlockFromMainThread(napi_env env, napi_value js_cb, void* context, void* data) { @@ -127,18 +301,28 @@ void JSMethodCallback(ffi_cif* cif, void* ret, void* args[], void* data) { argv[i] = closure->argTypes[i]->toJS(env, ctx->args[i + 1], 0); } - JSCallbackInner(closure, func, thisArg, argv, ctx->cif->nargs - 1, &ctx->done, ctx->ret); - - ctx->cv.notify_one(); + JSCallbackInner(closure, func, thisArg, argv, ctx->cif->nargs - 1, nullptr, ctx->ret); + if (ctx->useCondvar) { + { + std::lock_guard lock(ctx->mutex); + ctx->done = true; + } + ctx->cv.notify_one(); + } } void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { Closure* closure = (Closure*)data; napi_env env = closure->env; - -#ifdef ENABLE_JS_RUNTIME - NapiScope scope(env); -#endif + closure->retain(); + struct ClosureRetainGuard { + Closure* closure; + ~ClosureRetainGuard() { + if (closure != nullptr) { + closure->release(); + } + } + } retainGuard{closure}; auto currentThreadId = std::this_thread::get_id(); @@ -147,8 +331,12 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { ctx.ret = ret; ctx.args = args; ctx.done = false; + ctx.useCondvar = false; if (currentThreadId == closure->jsThreadId) { +#ifdef ENABLE_JS_RUNTIME + NapiScope scope(env); +#endif Closure::callBlockFromMainThread(env, get_ref_value(env, closure->func), closure, &ctx); } else { #ifndef ENABLE_JS_RUNTIME @@ -156,18 +344,37 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { assert(false && "Threadsafe functions are not supported"); } + ctx.useCondvar = true; napi_acquire_threadsafe_function(closure->tsfn); std::unique_lock lock(ctx.mutex); napi_call_threadsafe_function(closure->tsfn, &ctx, napi_tsfn_blocking); ctx.cv.wait(lock, [&ctx] { return ctx.done; }); napi_release_threadsafe_function(closure->tsfn, napi_tsfn_release); #else - Closure::callBlockFromMainThread(env, get_ref_value(env, closure->func), closure, &ctx); + auto runloop = closure->jsRunLoop; + if (runloop == nullptr) { + runloop = CFRunLoopGetMain(); + } + + if (runloop == nullptr) { + NapiScope scope(env); + Closure::callBlockFromMainThread(env, get_ref_value(env, closure->func), closure, &ctx); + } else { + JSBlockCallContext* ctxPtr = &ctx; + dispatch_semaphore_t done = dispatch_semaphore_create(0); + CFRunLoopPerformBlock(runloop, kCFRunLoopCommonModes, ^{ + NapiScope scope(env); + Closure::callBlockFromMainThread(env, get_ref_value(env, closure->func), closure, ctxPtr); + dispatch_semaphore_signal(done); + }); + CFRunLoopWakeUp(runloop); + dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER); + } #endif // ENABLE_JS_RUNTIME } } -Closure::Closure(std::string encoding, bool isBlock) { +Closure::Closure(std::string encoding, bool isBlock, bool isMethod) { auto signature = [NSMethodSignature signatureWithObjCTypes:encoding.c_str()]; size_t argc = signature.numberOfArguments; @@ -186,7 +393,7 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { for (int i = 0; i < argc; i++) { const char* argenc = [signature getArgumentTypeAtIndex:i]; auto argTypeInfo = TypeConv::Make(env, &argenc); - this->atypes[i + skipArgs] = argTypeInfo->type; + this->atypes[i + skipArgs] = argTypeInfo->ffiTypeForArgument(); this->argTypes.push_back(argTypeInfo); } @@ -200,7 +407,9 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { closure = (ffi_closure*)ffi_closure_alloc(sizeof(ffi_closure), &fnptr); - ffi_prep_closure_loc(closure, &cif, isBlock ? JSBlockCallback : JSMethodCallback, this, fnptr); + ffi_prep_closure_loc( + closure, &cif, isBlock ? JSBlockCallback : (isMethod ? JSMethodCallback : JSFunctionCallback), + this, fnptr); } Closure::Closure(MDMetadataReader* reader, MDSectionOffset offset, bool isBlock, @@ -242,7 +451,7 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { this->atypes[0] = &ffi_type_pointer; } for (int i = 0; i < argTypes.size(); i++) { - this->atypes[i + skipArgs] = argTypes[i]->type; + this->atypes[i + skipArgs] = argTypes[i]->ffiTypeForArgument(); } } @@ -255,7 +464,9 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { closure = (ffi_closure*)ffi_closure_alloc(sizeof(ffi_closure), &fnptr); - ffi_prep_closure_loc(closure, &cif, isBlock ? JSBlockCallback : JSMethodCallback, this, fnptr); + ffi_prep_closure_loc( + closure, &cif, isBlock ? JSBlockCallback : (isMethod ? JSMethodCallback : JSFunctionCallback), + this, fnptr); } Closure::~Closure() { @@ -276,4 +487,12 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) { ffi_closure_free(closure); } +void Closure::retain() { retainCount.fetch_add(1, std::memory_order_relaxed); } + +void Closure::release() { + if (retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) { + deleteClosureOnOwningThread(this); + } +} + } // namespace nativescript diff --git a/NativeScript/ffi/Enum.mm b/NativeScript/ffi/Enum.mm index bcbc233d..5e96d462 100644 --- a/NativeScript/ffi/Enum.mm +++ b/NativeScript/ffi/Enum.mm @@ -1,9 +1,63 @@ #include "Enum.h" #import +#include +#include +#include +#include #include "ObjCBridge.h" namespace nativescript { +namespace { +inline void defineConstantIfMissing(napi_env env, napi_value object, const std::string& name, + napi_value value, + napi_property_attributes attributes = napi_enumerable) { + bool hasProperty = false; + napi_has_named_property(env, object, name.c_str(), &hasProperty); + if (hasProperty) { + return; + } + + napi_property_descriptor prop = { + .utf8name = name.c_str(), + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = value, + .attributes = attributes, + .data = nullptr, + }; + napi_define_properties(env, object, 1, &prop); +} + +inline bool startsWith(const std::string& value, const std::string& prefix) { + return value.size() >= prefix.size() && value.compare(0, prefix.size(), prefix) == 0; +} + +inline std::string stripEnumSuffix(const std::string& enumName) { + static const std::vector suffixes = { + "Options", "Option", "Enums", "Enum", "Result", "Direction", "Orientation", + "Style", "Mask", "Type", "Status", "Modes", "Mode", "s"}; + + for (const auto& suffix : suffixes) { + if (enumName.size() > suffix.size() && + enumName.compare(enumName.size() - suffix.size(), suffix.size(), suffix) == 0) { + return enumName.substr(0, enumName.size() - suffix.size()); + } + } + + return enumName; +} + +inline bool isNSComparisonResultOrderingName(const std::string& enumName, + const std::string& member) { + if (enumName != "NSComparisonResult") { + return false; + } + return member == "Ascending" || member == "Same" || member == "Descending"; +} +} // namespace + void ObjCBridgeState::registerEnumGlobals(napi_env env, napi_value global) { MDSectionOffset offset = metadata->enumsOffset; while (offset < metadata->signaturesOffset) { @@ -15,8 +69,16 @@ while (next) { auto nameOffset = metadata->getOffset(offset); next = (nameOffset & mdSectionOffsetNext) != 0; + nameOffset &= ~mdSectionOffsetNext; + auto memberName = metadata->resolveString(nameOffset); offset += sizeof(MDSectionOffset); + int64_t value = metadata->getEnumValue(offset); offset += sizeof(int64_t); + + napi_value member; + napi_create_int64(env, value, &member); + defineConstantIfMissing(env, global, memberName, member, + (napi_property_attributes)(napi_enumerable | napi_configurable)); } napi_property_descriptor prop = { @@ -47,9 +109,16 @@ napi_value result; napi_create_object(env, &result); - // name + // enum name + auto enumName = bridgeState->metadata->getString(offset); + std::string enumNameStr = enumName; offset += sizeof(MDSectionOffset); + std::string strippedPrefix = stripEnumSuffix(enumNameStr); + + napi_value global; + napi_get_global(env, &global); + bool next = true; while (next) { auto nameOffset = bridgeState->metadata->getOffset(offset); @@ -63,17 +132,67 @@ napi_value member; napi_create_int64(env, value, &member); - napi_property_descriptor prop = { - .utf8name = name, + std::string memberName = name; + std::vector aliases; + aliases.push_back(memberName); + + if (!strippedPrefix.empty() && startsWith(memberName, strippedPrefix) && + memberName.size() > strippedPrefix.size()) { + aliases.push_back(memberName.substr(strippedPrefix.size())); + } else if (!strippedPrefix.empty() && !startsWith(memberName, strippedPrefix)) { + aliases.push_back(strippedPrefix + memberName); + } + + if (startsWith(enumNameStr, "NS") && !startsWith(memberName, "NS")) { + aliases.push_back(std::string("NS") + memberName); + } + + if (enumNameStr == "NSStringCompareOptions" && !memberName.ends_with("Search")) { + aliases.push_back(memberName + "Search"); + aliases.push_back(std::string("NS") + memberName + "Search"); + } + + if (!startsWith(memberName, "k")) { + aliases.push_back(std::string("k") + enumNameStr + memberName); + } + + if (isNSComparisonResultOrderingName(enumNameStr, memberName)) { + aliases.push_back(std::string("Ordered") + memberName); + aliases.push_back(std::string("NSOrdered") + memberName); + } + + std::vector uniqueAliases; + uniqueAliases.reserve(aliases.size()); + std::unordered_set seenAliases; + for (const auto& alias : aliases) { + if (seenAliases.insert(alias).second) { + uniqueAliases.push_back(alias); + } + } + + for (const auto& alias : uniqueAliases) { + defineConstantIfMissing(env, result, alias, member); + defineConstantIfMissing(env, global, alias, member, + (napi_property_attributes)(napi_enumerable | napi_configurable)); + } + + std::string reverseCanonical = uniqueAliases.size() > 1 ? uniqueAliases[1] : memberName; + + // reverse mapping enum[value] -> canonical member name + char valueKey[32]; + snprintf(valueKey, sizeof(valueKey), "%lld", (long long)value); + napi_value reverseName; + napi_create_string_utf8(env, reverseCanonical.c_str(), NAPI_AUTO_LENGTH, &reverseName); + napi_property_descriptor reverseProp = { + .utf8name = valueKey, .method = nullptr, .getter = nullptr, .setter = nullptr, - .value = member, + .value = reverseName, .attributes = napi_enumerable, .data = nullptr, }; - - napi_define_properties(env, result, 1, &prop); + napi_define_properties(env, result, 1, &reverseProp); } bridgeState->mdValueCache[originalOffset] = make_ref(env, result); @@ -81,4 +200,4 @@ return result; } -} // namespace nativescript \ No newline at end of file +} // namespace nativescript diff --git a/NativeScript/ffi/Interop.h b/NativeScript/ffi/Interop.h index a806cef2..91de18aa 100644 --- a/NativeScript/ffi/Interop.h +++ b/NativeScript/ffi/Interop.h @@ -31,6 +31,10 @@ class Pointer { static napi_value add(napi_env env, napi_callback_info info); static napi_value subtract(napi_env env, napi_callback_info info); static napi_value toNumber(napi_env env, napi_callback_info info); + static napi_value toBigInt(napi_env env, napi_callback_info info); + static napi_value toHexString(napi_env env, napi_callback_info info); + static napi_value toDecimalString(napi_env env, napi_callback_info info); + static napi_value toString(napi_env env, napi_callback_info info); static napi_value customInspect(napi_env env, napi_callback_info info); static void finalize(napi_env env, void* data, void* hint); @@ -46,6 +50,8 @@ class Reference { public: static napi_value defineJSClass(napi_env env); static bool isInstance(napi_env env, napi_value value); + static napi_value create(napi_env env, std::shared_ptr type, void* data, + bool ownsData = false); static Reference* unwrap(napi_env env, napi_value value); @@ -70,6 +76,7 @@ class Reference { class FunctionReference { public: static napi_value defineJSClass(napi_env env); + static bool isInstance(napi_env env, napi_value value); static FunctionReference* unwrap(napi_env env, napi_value value); @@ -80,7 +87,7 @@ class FunctionReference { FunctionReference(napi_env env, napi_ref ref) : env(env), ref(ref) {}; ~FunctionReference(); - void* getFunctionPointer(MDSectionOffset offset); + void* getFunctionPointer(MDSectionOffset offset, bool isBlock = false); napi_env env; napi_ref ref; diff --git a/NativeScript/ffi/Interop.mm b/NativeScript/ffi/Interop.mm index fea23818..99df0e90 100644 --- a/NativeScript/ffi/Interop.mm +++ b/NativeScript/ffi/Interop.mm @@ -8,10 +8,479 @@ #include "node_api_util.h" #import +#include +#include +#include +#include +#include +#include #include +#include +#include namespace nativescript { +namespace { +std::unordered_map g_pointerCache; +constexpr const char* kNativePointerProperty = "__ns_native_ptr"; +constexpr const char* kFunctionReferenceMarker = "__ns_function_reference"; +constexpr const char* kFunctionReferenceDataProperty = "__ns_function_reference_data"; + +inline uintptr_t pointerKey(void* data) { return reinterpret_cast(data); } + +inline bool isInteropTypeCode(int32_t value) { + switch (value) { + case mdTypeVoid: + case mdTypeBool: + case mdTypeChar: + case mdTypeUInt8: + case mdTypeSShort: + case mdTypeUShort: + case mdTypeSInt: + case mdTypeUInt: + case mdTypeSInt64: + case mdTypeUInt64: + case mdTypeFloat: + case mdTypeDouble: + case mdTypeString: + case mdTypeAnyObject: + case mdTypePointer: + case mdTypeSelector: + return true; + default: + return false; + } +} + +inline size_t referenceElementSize(Reference* ref) { + if (ref == nullptr || ref->type == nullptr || ref->type->type == nullptr || + ref->type->type->size == 0) { + return sizeof(void*); + } + + return ref->type->type->size; +} + +inline bool isArrayIndexProperty(napi_env env, napi_value property, uint32_t* index) { + if (index == nullptr) { + return false; + } + + napi_valuetype propertyType = napi_undefined; + napi_typeof(env, property, &propertyType); + + if (propertyType == napi_number) { + double number = 0; + if (napi_get_value_double(env, property, &number) != napi_ok) { + return false; + } + + if (!std::isfinite(number) || number < 0 || floor(number) != number || + number > static_cast(UINT32_MAX)) { + return false; + } + + *index = static_cast(number); + return true; + } + + if (propertyType != napi_string) { + return false; + } + + char chars[32]; + size_t length = 0; + if (napi_get_value_string_utf8(env, property, chars, sizeof(chars), &length) != napi_ok || + length == 0 || length >= sizeof(chars)) { + return false; + } + + for (size_t i = 0; i < length; i++) { + if (!std::isdigit(static_cast(chars[i]))) { + return false; + } + } + + char* end = nullptr; + unsigned long long parsed = std::strtoull(chars, &end, 10); + if (end == nullptr || *end != '\0' || parsed > static_cast(UINT32_MAX)) { + return false; + } + + *index = static_cast(parsed); + return true; +} + +inline napi_value referenceValueAtIndex(napi_env env, Reference* ref, uint32_t index) { + napi_value undefined; + napi_get_undefined(env, &undefined); + + if (ref == nullptr || ref->type == nullptr || ref->data == nullptr) { + return undefined; + } + + auto slot = + static_cast(ref->data) + (static_cast(index) * referenceElementSize(ref)); + napi_value value = ref->type->toJS(env, slot); + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType == napi_boolean) { + bool boolValue = false; + napi_get_value_bool(env, value, &boolValue); + napi_create_uint32(env, boolValue ? 1 : 0, &value); + } + return value; +} + +inline bool referenceSetValueAtIndex(napi_env env, Reference* ref, uint32_t index, + napi_value value) { + if (ref == nullptr || ref->type == nullptr || ref->data == nullptr) { + napi_throw_error(env, nullptr, "Reference is not initialized"); + return false; + } + + auto slot = + static_cast(ref->data) + (static_cast(index) * referenceElementSize(ref)); + bool shouldFree = false; + ref->type->toNative(env, value, slot, &shouldFree, &shouldFree); + + bool hasPendingException = false; + napi_is_exception_pending(env, &hasPendingException); + return !hasPendingException; +} + +napi_value referenceProxyGetter(napi_env env, napi_callback_info info) { + size_t argc = 3; + napi_value argv[3] = {nullptr, nullptr, nullptr}; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + napi_value target = argc > 0 ? argv[0] : nullptr; + napi_value property = argc > 1 ? argv[1] : nullptr; + + uint32_t index = 0; + if (property != nullptr && isArrayIndexProperty(env, property, &index)) { + return referenceValueAtIndex(env, Reference::unwrap(env, target), index); + } + + napi_value result; + if (napi_get_property(env, target, property, &result) != napi_ok) { + return nullptr; + } + + napi_valuetype resultType = napi_undefined; + napi_typeof(env, result, &resultType); + if (resultType == napi_function) { + napi_value bind; + if (napi_get_named_property(env, result, "bind", &bind) == napi_ok) { + napi_value bound; + napi_value bindArgs[1] = {target}; + if (napi_call_function(env, result, bind, 1, bindArgs, &bound) == napi_ok) { + return bound; + } + } + } + + return result; +} + +napi_value referenceProxySetter(napi_env env, napi_callback_info info) { + size_t argc = 4; + napi_value argv[4] = {nullptr, nullptr, nullptr, nullptr}; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + napi_value target = argc > 0 ? argv[0] : nullptr; + napi_value property = argc > 1 ? argv[1] : nullptr; + napi_value value = argc > 2 ? argv[2] : nullptr; + + uint32_t index = 0; + bool ok = true; + if (property != nullptr && isArrayIndexProperty(env, property, &index)) { + ok = referenceSetValueAtIndex(env, Reference::unwrap(env, target), index, value); + } else { + ok = napi_set_property(env, target, property, value) == napi_ok; + } + + napi_value result; + napi_get_boolean(env, ok, &result); + return result; +} + +inline napi_value createReferenceProxy(napi_env env, napi_value target, Reference* reference) { + napi_value global; + napi_get_global(env, &global); + + napi_value proxyCtor; + if (napi_get_named_property(env, global, "Proxy", &proxyCtor) != napi_ok) { + return target; + } + + napi_value handler; + napi_create_object(env, &handler); + const napi_property_descriptor handlerProperties[] = { + { + .utf8name = "get", + .method = referenceProxyGetter, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_default, + .data = nullptr, + }, + { + .utf8name = "set", + .method = referenceProxySetter, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_default, + .data = nullptr, + }, + }; + napi_define_properties(env, handler, 2, handlerProperties); + + napi_value proxyArgs[2] = {target, handler}; + napi_value proxy; + if (napi_new_instance(env, proxyCtor, 2, proxyArgs, &proxy) != napi_ok) { + return target; + } + + napi_wrap(env, proxy, reference, nullptr, nullptr, nullptr); + + return proxy; +} + +inline bool getCachedPointer(napi_env env, void* data, napi_value* value) { + auto it = g_pointerCache.find(pointerKey(data)); + if (it == g_pointerCache.end()) { + return false; + } + + *value = get_ref_value(env, it->second); + if (*value == nullptr) { + napi_delete_reference(env, it->second); + g_pointerCache.erase(it); + return false; + } + + Pointer* ptr = Pointer::unwrap(env, *value); + if (ptr == nullptr || ptr->data != data) { + napi_delete_reference(env, it->second); + g_pointerCache.erase(it); + *value = nullptr; + return false; + } + + return true; +} + +inline void cachePointer(napi_env env, void* data, napi_value value) { + const uintptr_t key = pointerKey(data); + if (g_pointerCache.find(key) != g_pointerCache.end()) { + return; + } + napi_ref ref = nullptr; + napi_create_reference(env, value, 1, &ref); + g_pointerCache[key] = ref; +} + +inline std::string pointerHexString(void* data) { + uintptr_t value = reinterpret_cast(data); + if (value == 0) { + return "0x0"; + } + + char hex[2 + sizeof(uintptr_t) * 2 + 1]; +#if UINTPTR_MAX == 0xffffffff + snprintf(hex, sizeof(hex), "0x%x", static_cast(value)); +#else + snprintf(hex, sizeof(hex), "0x%llx", static_cast(value)); +#endif + return std::string(hex); +} + +inline void* lookupSymbolByName(ObjCBridgeState* bridgeState, const char* symbolName) { + void* fn = dlsym(bridgeState->self_dl, symbolName); + if (fn == nullptr) { + fn = dlsym(RTLD_DEFAULT, symbolName); + } + if (fn == nullptr) { + std::string underscored = "_"; + underscored += symbolName; + fn = dlsym(bridgeState->self_dl, underscored.c_str()); + if (fn == nullptr) { + fn = dlsym(RTLD_DEFAULT, underscored.c_str()); + } + } + return fn; +} + +inline bool unwrapKnownNativeHandle(napi_env env, napi_value value, void** out) { + if (value == nullptr) { + return false; + } + + if (Pointer::isInstance(env, value)) { + Pointer* ptr = Pointer::unwrap(env, value); + *out = ptr != nullptr ? ptr->data : nullptr; + return true; + } + + if (Reference::isInstance(env, value)) { + Reference* ref = Reference::unwrap(env, value); + *out = ref != nullptr ? ref->data : nullptr; + return true; + } + + if (StructObject* structObject = StructObject::unwrap(env, value)) { + *out = structObject->data; + return structObject->data != nullptr; + } + + void* wrapped = nullptr; + if (napi_unwrap(env, value, &wrapped) != napi_ok || wrapped == nullptr) { + bool hasNativePointer = false; + napi_has_named_property(env, value, kNativePointerProperty, &hasNativePointer); + if (hasNativePointer) { + napi_value nativePointerValue; + if (napi_get_named_property(env, value, kNativePointerProperty, &nativePointerValue) == + napi_ok) { + void* nativePointer = nullptr; + if (napi_get_value_external(env, nativePointerValue, &nativePointer) == napi_ok && + nativePointer != nullptr) { + *out = nativePointer; + return true; + } + } + } + + return false; + } + + ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); + for (const auto& entry : bridgeState->classes) { + if (entry.second == wrapped) { + *out = (void*)entry.second->nativeClass; + return true; + } + } + + for (const auto& entry : bridgeState->protocols) { + if (entry.second == wrapped) { + *out = (void*)objc_getProtocol(entry.second->name.c_str()); + return true; + } + } + + for (const auto& entry : bridgeState->cFunctionCache) { + if (entry.second == wrapped) { + *out = entry.second->fnptr; + return true; + } + } + + *out = wrapped; + return true; +} + +inline bool resolveNativePointerFromRegisteredFunction(napi_env env, napi_value value, void** out) { + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState == nullptr || bridgeState->metadata == nullptr) { + return false; + } + + napi_value global; + if (napi_get_global(env, &global) != napi_ok) { + return false; + } + + MDSectionOffset offset = bridgeState->metadata->functionsOffset; + while (offset < bridgeState->metadata->protocolsOffset) { + MDSectionOffset originalOffset = offset; + const char* functionName = bridgeState->metadata->getString(offset); + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + offset += sizeof(MDFunctionFlag); + + napi_value globalFn; + if (napi_get_named_property(env, global, functionName, &globalFn) != napi_ok) { + continue; + } + + bool isSameFunction = false; + napi_strict_equals(env, value, globalFn, &isSameFunction); + if (!isSameFunction) { + continue; + } + + auto cFunction = bridgeState->getCFunction(env, originalOffset); + if (cFunction != nullptr && cFunction->fnptr != nullptr) { + *out = cFunction->fnptr; + return true; + } + + void* fn = lookupSymbolByName(bridgeState, functionName); + if (fn != nullptr) { + *out = fn; + return true; + } + return false; + } + + return false; +} + +inline bool resolveNativePointerFromFunctionName(napi_env env, napi_value value, void** out) { + napi_valuetype type = napi_undefined; + napi_typeof(env, value, &type); + if (type != napi_function) { + return false; + } + + napi_value nameValue; + if (napi_get_named_property(env, value, "name", &nameValue) != napi_ok) { + return false; + } + + napi_valuetype nameType = napi_undefined; + napi_typeof(env, nameValue, &nameType); + if (nameType != napi_string) { + return false; + } + + char name[512]; + size_t len = 0; + if (napi_get_value_string_utf8(env, nameValue, name, sizeof(name), &len) != napi_ok || len == 0) { + return resolveNativePointerFromRegisteredFunction(env, value, out); + } + + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState == nullptr) { + return resolveNativePointerFromRegisteredFunction(env, value, out); + } + void* fn = lookupSymbolByName(bridgeState, name); + if (fn == nullptr) { + return resolveNativePointerFromRegisteredFunction(env, value, out); + } + + *out = fn; + return true; +} + +inline void setObjectPrototype(napi_env env, napi_value object, napi_value prototype) { + napi_value global; + napi_get_global(env, &global); + + napi_value jsObject; + napi_get_named_property(env, global, "Object", &jsObject); + + napi_value setPrototypeOf; + napi_get_named_property(env, jsObject, "setPrototypeOf", &setPrototypeOf); + + napi_value argv[2] = {object, prototype}; + napi_call_function(env, jsObject, setPrototypeOf, 2, argv, nullptr); +} +} // namespace + inline napi_value createJSNumber(napi_env env, int32_t ival) { napi_value value; napi_create_int32(env, ival, &value); @@ -49,15 +518,36 @@ napi_value __extends(napi_env env, napi_callback_info info) { }; } + if (typeof globalThis.__param !== "function") { + globalThis.__param = function(paramIndex, decorator) { + return function(target, key) { decorator(target, key, paramIndex); }; + }; + } + globalThis.ObjCClass = function ObjCClass(...protocols) { return function(constructor) { - constructor.ObjCProtocols = protocols; + if (constructor.ObjCProtocols) { + constructor.ObjCProtocols.push(...protocols); + } else { + constructor.ObjCProtocols = protocols; + } + + if (typeof globalThis.NativeClass === "function") { + return globalThis.NativeClass(constructor); + } + + return constructor; }; }; - WeakRef.prototype.get = function() { - return this.deref(); - }; + if (typeof WeakRef === "function") { + WeakRef.prototype.get = WeakRef.prototype.deref; + if (!WeakRef.prototype.clear) { + WeakRef.prototype.clear = function() { + console.warn("WeakRef.clear() is non-standard and has been deprecated. It does nothing and the call can be safely removed."); + }; + } + } )"; void registerInterop(napi_env env, napi_value global) { @@ -99,11 +589,12 @@ void registerInterop(napi_env env, napi_value global) { napi_set_named_property(env, types, "float", createJSNumber(env, mdTypeFloat)); napi_set_named_property(env, types, "double", createJSNumber(env, mdTypeDouble)); napi_set_named_property(env, types, "UTF8CString", createJSNumber(env, mdTypeString)); - napi_set_named_property(env, types, "unichar", createJSNumber(env, mdTypeString)); + napi_set_named_property(env, types, "unichar", createJSNumber(env, mdTypeUShort)); napi_set_named_property(env, types, "id", createJSNumber(env, mdTypeAnyObject)); napi_set_named_property(env, types, "protocol", createJSNumber(env, mdTypePointer)); napi_set_named_property(env, types, "class", createJSNumber(env, mdTypeAnyObject)); napi_set_named_property(env, types, "SEL", createJSNumber(env, mdTypeSelector)); + napi_set_named_property(env, types, "selector", createJSNumber(env, mdTypeSelector)); napi_set_named_property(env, types, "pointer", createJSNumber(env, mdTypePointer)); napi_value Pointer = Pointer::defineJSClass(env); @@ -113,6 +604,7 @@ void registerInterop(napi_env env, napi_value global) { bridgeState->referenceClass = make_ref(env, Reference); napi_value FunctionReference = FunctionReference::defineJSClass(env); + bridgeState->functionReferenceClass = make_ref(env, FunctionReference); const napi_property_descriptor properties[] = { { @@ -328,6 +820,11 @@ napi_value interop_free(napi_env env, napi_callback_info info) { napi_unwrap(env, arg, (void**)&ptr); if (ptr != nullptr && ptr->data != nullptr) { + auto it = g_pointerCache.find(pointerKey(ptr->data)); + if (it != g_pointerCache.end()) { + napi_delete_reference(env, it->second); + g_pointerCache.erase(it); + } free(ptr->data); ptr->data = nullptr; } @@ -336,9 +833,11 @@ napi_value interop_free(napi_env env, napi_callback_info info) { } napi_value interop_stringFromCString(napi_env env, napi_callback_info info) { - napi_value arg; - size_t argc = 1; - napi_get_cb_info(env, info, &argc, &arg, nullptr, nullptr); + size_t argc = 2; + napi_value argv[2] = {nullptr, nullptr}; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + napi_value arg = argv[0]; napi_valuetype type; napi_typeof(env, arg, &type); @@ -369,7 +868,20 @@ napi_value interop_stringFromCString(napi_env env, napi_callback_info info) { if (data == nullptr) { napi_get_null(env, &result); } else { - napi_create_string_utf8(env, (const char*)data, NAPI_AUTO_LENGTH, &result); + size_t length = NAPI_AUTO_LENGTH; + if (argc >= 2 && argv[1] != nullptr) { + napi_valuetype lengthType = napi_undefined; + napi_typeof(env, argv[1], &lengthType); + if (lengthType != napi_undefined && lengthType != napi_null) { + int64_t explicitLength = 0; + napi_get_value_int64(env, argv[1], &explicitLength); + if (explicitLength < 0) { + explicitLength = 0; + } + length = static_cast(explicitLength); + } + } + napi_create_string_utf8(env, (const char*)data, length, &result); } return result; @@ -433,16 +945,25 @@ size_t jsSizeof(napi_env env, napi_value arg) { case napi_function: { napi_value symbolSizeof = jsSymbolFor(env, "sizeof"); napi_value result; - napi_get_property(env, arg, symbolSizeof, &result); - napi_valuetype resultType; - napi_typeof(env, result, &resultType); - if (resultType == napi_number) { - int32_t size; - napi_get_value_int32(env, result, &size); - return size; - } else { - napi_throw_type_error(env, "TypeError", "Invalid type for sizeof"); + if (napi_get_property(env, arg, symbolSizeof, &result) == napi_ok) { + napi_valuetype resultType; + napi_typeof(env, result, &resultType); + if (resultType == napi_number) { + int32_t size; + napi_get_value_int32(env, result, &size); + return size; + } + } + + void* nativeHandle = nullptr; + if (unwrapKnownNativeHandle(env, arg, &nativeHandle)) { + return sizeof(void*); + } + if (resolveNativePointerFromFunctionName(env, arg, &nativeHandle)) { + return sizeof(void*); } + + napi_throw_type_error(env, "TypeError", "Invalid type for sizeof"); } default: @@ -473,23 +994,8 @@ napi_value interop_alloc(napi_env env, napi_callback_info info) { int64_t size; napi_get_value_int64(env, arg, &size); - void* data = malloc(size); - - napi_value PointerClass = get_ref_value(env, ObjCBridgeState::InstanceData(env)->pointerClass); - napi_value result; - napi_new_instance(env, PointerClass, 0, nullptr, &result); - - Pointer* ptr = nullptr; - napi_unwrap(env, result, (void**)&ptr); - - if (ptr == nullptr) { - napi_throw_error(env, nullptr, "Invalid pointer"); - return nullptr; - } - - ptr->data = data; - - return result; + void* data = calloc(1, size); + return Pointer::create(env, data); } napi_value interop_handleof(napi_env env, napi_callback_info info) { @@ -497,17 +1003,52 @@ napi_value interop_handleof(napi_env env, napi_callback_info info) { size_t argc = 1; napi_get_cb_info(env, info, &argc, &value, nullptr, nullptr); + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType == napi_null || valueType == napi_undefined) { + napi_get_null(env, &result); + return result; + } + + if (valueType == napi_string) { + size_t len = 0; + napi_get_value_string_utf8(env, value, nullptr, 0, &len); + char* str = static_cast(malloc(len + 1)); + napi_get_value_string_utf8(env, value, str, len + 1, &len); + str[len] = '\0'; + return Pointer::create(env, str); + } + if (Pointer::isInstance(env, value)) { return value; } else if (Reference::isInstance(env, value)) { Reference* ref = Reference::unwrap(env, value); + if (ref == nullptr || ref->data == nullptr) { + napi_throw_error(env, nullptr, "Reference is not initialized"); + return nullptr; + } return Pointer::create(env, ref->data); } - void* data = nullptr; - napi_unwrap(env, value, (void**)&data); + if (FunctionReference::isInstance(env, value)) { + FunctionReference* reference = FunctionReference::unwrap(env, value); + if (reference == nullptr || reference->closure == nullptr || + reference->closure->fnptr == nullptr) { + napi_throw_error(env, nullptr, "FunctionReference is not initialized"); + return nullptr; + } + return Pointer::create(env, reference->closure->fnptr); + } - if (data != nullptr) { + void* data = nullptr; + if (unwrapKnownNativeHandle(env, value, &data) && data != nullptr) { + if (valueType == napi_object && !Pointer::isInstance(env, value) && + !Reference::isInstance(env, value) && !FunctionReference::isInstance(env, value)) { + ObjCBridgeState::InstanceData(env)->cacheHandleObject(env, data, value); + } + return Pointer::create(env, data); + } + if (resolveNativePointerFromFunctionName(env, value, &data) && data != nullptr) { return Pointer::create(env, data); } @@ -520,8 +1061,27 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { size_t argc = 1; napi_get_cb_info(env, info, &argc, &arg, nullptr, nullptr); + bool isArrayBuffer = false; + napi_is_arraybuffer(env, arg, &isArrayBuffer); + if (isArrayBuffer) { + return arg; + } + NSData* data = nil; - napi_unwrap(env, arg, (void**)&data); + if (napi_unwrap(env, arg, (void**)&data) != napi_ok || data == nil) { + void* native = nullptr; + if (!unwrapKnownNativeHandle(env, arg, &native) || native == nullptr) { + napi_throw_error(env, nullptr, "Invalid data"); + return nullptr; + } + + id candidate = (id)native; + if (![candidate isKindOfClass:[NSData class]]) { + napi_throw_error(env, nullptr, "Invalid data"); + return nullptr; + } + data = (NSData*)candidate; + } if (data == nil) { napi_throw_error(env, nullptr, "Invalid data"); @@ -564,6 +1124,42 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { .attributes = napi_enumerable, .data = nullptr, }, + { + .utf8name = "toBigInt", + .method = Pointer::toBigInt, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_enumerable, + .data = nullptr, + }, + { + .utf8name = "toHexString", + .method = Pointer::toHexString, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_enumerable, + .data = nullptr, + }, + { + .utf8name = "toDecimalString", + .method = Pointer::toDecimalString, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_enumerable, + .data = nullptr, + }, + { + .utf8name = "toString", + .method = Pointer::toString, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_enumerable, + .data = nullptr, + }, { .utf8name = nullptr, .name = jsSymbolFor(env, "nodejs.util.inspect.custom"), @@ -577,9 +1173,18 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { }; napi_value constructor; - napi_define_class(env, "Pointer", NAPI_AUTO_LENGTH, Pointer::constructor, nullptr, 4, properties, + napi_define_class(env, "Pointer", NAPI_AUTO_LENGTH, Pointer::constructor, nullptr, 8, properties, &constructor); + napi_value symbolSizeof = jsSymbolFor(env, "sizeof"); + napi_value sizeValue; + napi_create_int32(env, sizeof(void*), &sizeValue); + napi_set_property(env, constructor, symbolSizeof, sizeValue); + + napi_value prototype; + napi_get_named_property(env, constructor, "prototype", &prototype); + napi_set_property(env, prototype, symbolSizeof, sizeValue); + return constructor; } @@ -592,26 +1197,22 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { } napi_value Pointer::create(napi_env env, void* data) { - if (data == nullptr) { - printf("Pointer::create called with null data\n"); - return nullptr; + napi_value cached; + if (getCachedPointer(env, data, &cached)) { + return cached; } + ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); - bool isInstance = false; napi_value jsPointer = get_ref_value(env, bridgeState->pointerClass); + napi_value argv[1]; + napi_create_bigint_uint64(env, static_cast(reinterpret_cast(data)), + &argv[0]); napi_value result; - napi_status status = napi_new_instance(env, jsPointer, 0, nullptr, &result); + napi_status status = napi_new_instance(env, jsPointer, 1, argv, &result); if (status != napi_ok) { return nullptr; } - - Pointer* ptr = Pointer::unwrap(env, result); - if (ptr == nullptr) { - return nullptr; - } - - ptr->data = data; return result; } @@ -624,19 +1225,38 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { napi_value Pointer::constructor(napi_env env, napi_callback_info info) { napi_value jsThis; size_t argc = 1; + napi_value argv[1]; + napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr); + napi_value arg; - napi_get_cb_info(env, info, &argc, &arg, &jsThis, nullptr); + if (argc == 0) { + napi_get_undefined(env, &arg); + } else { + arg = argv[0]; + } - napi_valuetype type; + napi_valuetype type = napi_undefined; napi_typeof(env, arg, &type); void* data; switch (type) { case napi_number: { - int64_t ival; - napi_get_value_int64(env, arg, &ival); - data = (void*)ival; + double number = 0; + napi_get_value_double(env, arg, &number); + if (!std::isfinite(number)) { + napi_throw_error(env, nullptr, "Invalid type"); + return nullptr; + } + data = (void*)((intptr_t)number); + break; + } + + case napi_bigint: { + int64_t value = 0; + bool lossless = false; + napi_get_value_bigint_int64(env, arg, &value, &lossless); + data = (void*)((intptr_t)value); break; } @@ -645,14 +1265,26 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { if (isInstance) { Pointer* ptr; napi_unwrap(env, arg, (void**)&ptr); - data = ptr->data; + if (ptr == nullptr) { + napi_throw_error(env, nullptr, "Invalid type"); + return nullptr; + } + return arg; } else { - napi_throw_error(env, nullptr, "Invalid type"); - return nullptr; + napi_value numberValue; + napi_coerce_to_number(env, arg, &numberValue); + double number = 0; + napi_get_value_double(env, numberValue, &number); + if (!std::isfinite(number)) { + napi_throw_error(env, nullptr, "Invalid type"); + return nullptr; + } + data = (void*)((intptr_t)number); } break; } + case napi_null: case napi_undefined: { data = nullptr; break; @@ -663,9 +1295,15 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { return nullptr; } + napi_value cached; + if (getCachedPointer(env, data, &cached)) { + return cached; + } + Pointer* ptr = new Pointer(data); napi_ref ref; napi_wrap(env, jsThis, ptr, Pointer::finalize, nullptr, &ref); + cachePointer(env, data, jsThis); return jsThis; } @@ -680,14 +1318,8 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { int64_t ival; napi_get_value_int64(env, arg, &ival); - napi_value PointerClass = get_ref_value(env, ObjCBridgeState::InstanceData(env)->pointerClass); - napi_value result; - napi_new_instance(env, PointerClass, 0, nullptr, &result); - - Pointer* resultPtr = Pointer::unwrap(env, result); - resultPtr->data = (void*)((int64_t)ptr->data + ival); - - return result; + void* newData = (void*)((intptr_t)((intptr_t)ptr->data + (intptr_t)ival)); + return Pointer::create(env, newData); } napi_value Pointer::subtract(napi_env env, napi_callback_info info) { @@ -700,39 +1332,75 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { int64_t ival; napi_get_value_int64(env, arg, &ival); - napi_value PointerClass = get_ref_value(env, ObjCBridgeState::InstanceData(env)->pointerClass); + void* newData = (void*)((intptr_t)((intptr_t)ptr->data - (intptr_t)ival)); + return Pointer::create(env, newData); +} + +napi_value Pointer::toNumber(napi_env env, napi_callback_info info) { + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + + Pointer* ptr = Pointer::unwrap(env, jsThis); + napi_value result; - napi_new_instance(env, PointerClass, 0, nullptr, &result); + double value = static_cast(reinterpret_cast(ptr->data)); + napi_create_double(env, value, &result); + return result; +} - Pointer* resultPtr = Pointer::unwrap(env, result); - resultPtr->data = (void*)((int64_t)ptr->data - ival); +napi_value Pointer::toBigInt(napi_env env, napi_callback_info info) { + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + + Pointer* ptr = Pointer::unwrap(env, jsThis); + napi_value result; + napi_create_bigint_uint64(env, static_cast(reinterpret_cast(ptr->data)), + &result); return result; } -napi_value Pointer::toNumber(napi_env env, napi_callback_info info) { +napi_value Pointer::toHexString(napi_env env, napi_callback_info info) { napi_value jsThis; napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); Pointer* ptr = Pointer::unwrap(env, jsThis); + std::string hex = pointerHexString(ptr->data); napi_value result; - napi_create_int64(env, (int64_t)ptr->data, &result); + napi_create_string_utf8(env, hex.c_str(), NAPI_AUTO_LENGTH, &result); + return result; +} + +napi_value Pointer::toDecimalString(napi_env env, napi_callback_info info) { + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + Pointer* ptr = Pointer::unwrap(env, jsThis); + std::string decimal; + if (sizeof(void*) == 4) { + decimal = std::to_string(static_cast(reinterpret_cast(ptr->data))); + } else { + decimal = std::to_string(static_cast(reinterpret_cast(ptr->data))); + } + + napi_value result; + napi_create_string_utf8(env, decimal.c_str(), NAPI_AUTO_LENGTH, &result); return result; } +napi_value Pointer::toString(napi_env env, napi_callback_info info) { + return Pointer::customInspect(env, info); +} + napi_value Pointer::customInspect(napi_env env, napi_callback_info info) { napi_value jsThis; napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); Pointer* ptr = Pointer::unwrap(env, jsThis); + std::string str = "data) + ">"; napi_value result; - char* hex = (char*)malloc(17); - snprintf(hex, 17, "%0016llx", (uint64_t)ptr->data); - std::string str = ""; - free(hex); napi_create_string_utf8(env, str.c_str(), NAPI_AUTO_LENGTH, &result); return result; @@ -740,6 +1408,11 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { void Pointer::finalize(napi_env env, void* data, void* hint) { Pointer* ptr = (Pointer*)data; + auto it = g_pointerCache.find(pointerKey(ptr->data)); + if (it != g_pointerCache.end()) { + napi_delete_reference(env, it->second); + g_pointerCache.erase(it); + } delete ptr; } @@ -772,12 +1445,30 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { .attributes = napi_enumerable, .data = nullptr, }, + { + .utf8name = "toString", + .method = Reference::customInspect, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_enumerable, + .data = nullptr, + }, }; napi_value constructor; - napi_define_class(env, "Reference", NAPI_AUTO_LENGTH, Reference::constructor, nullptr, 2, + napi_define_class(env, "Reference", NAPI_AUTO_LENGTH, Reference::constructor, nullptr, 3, properties, &constructor); + napi_value symbolSizeof = jsSymbolFor(env, "sizeof"); + napi_value sizeValue; + napi_create_int32(env, sizeof(void*), &sizeValue); + napi_set_property(env, constructor, symbolSizeof, sizeValue); + + napi_value prototype; + napi_get_named_property(env, constructor, "prototype", &prototype); + napi_set_property(env, prototype, symbolSizeof, sizeValue); + return constructor; } @@ -789,9 +1480,33 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { return isInstance; } +napi_value Reference::create(napi_env env, std::shared_ptr type, void* data, + bool ownsData) { + ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); + napi_value referenceCtor = get_ref_value(env, bridgeState->referenceClass); + napi_value jsReference = nullptr; + if (napi_new_instance(env, referenceCtor, 0, nullptr, &jsReference) != napi_ok) { + return nullptr; + } + + Reference* reference = Reference::unwrap(env, jsReference); + if (reference == nullptr) { + return nullptr; + } + + reference->env = env; + reference->type = std::move(type); + reference->data = data; + reference->ownsData = ownsData; + + return jsReference; +} + Reference* Reference::unwrap(napi_env env, napi_value value) { Reference* ref = nullptr; - napi_unwrap(env, value, (void**)&ref); + if (napi_unwrap(env, value, (void**)&ref) != napi_ok) { + return nullptr; + } return ref; } @@ -804,8 +1519,42 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { Reference* reference = new Reference(); reference->env = env; - if (argc == 1) { - reference->initValue = make_ref(env, argv[0]); + if (argc == 0) { + // Leave it uninitialized. It can be materialized later by pointer marshalling. + } else if (argc == 1) { + napi_valuetype argType = napi_undefined; + napi_typeof(env, argv[0], &argType); + + bool isTypeArg = false; + if (argType == napi_function) { + isTypeArg = true; + } else if (argType == napi_number) { + double numericValue = 0; + if (napi_get_value_double(env, argv[0], &numericValue) == napi_ok && + std::isfinite(numericValue) && floor(numericValue) == numericValue && + numericValue >= static_cast(INT32_MIN) && + numericValue <= static_cast(INT32_MAX)) { + int32_t typeCode = static_cast(numericValue); + if (isInteropTypeCode(typeCode)) { + isTypeArg = true; + } + } + } + + if (isTypeArg) { + std::string typeEncoding = getEncodedType(env, argv[0]); + const char* typestr = typeEncoding.c_str(); + reference->type = TypeConv::Make(env, &typestr); + + size_t size = reference->type != nullptr && reference->type->type != nullptr && + reference->type->type->size > 0 + ? reference->type->type->size + : sizeof(void*); + reference->data = calloc(1, size); + reference->ownsData = true; + } else { + reference->initValue = make_ref(env, argv[0]); + } } else if (argc == 2) { std::string type = getEncodedType(env, argv[0]); const char* typestr = type.c_str(); @@ -817,17 +1566,39 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { if (argtype == napi_object && Pointer::isInstance(env, argv[1])) { reference->data = Pointer::unwrap(env, argv[1])->data; reference->ownsData = false; + } else if (reference->type != nullptr && reference->type->kind == mdTypeStruct && + argtype == napi_object && StructObject::isInstance(env, argv[1])) { + StructObject* structObject = StructObject::unwrap(env, argv[1]); + if (structObject != nullptr) { + reference->data = structObject->data; + reference->ownsData = false; + } + } else if (argtype == napi_object && Reference::isInstance(env, argv[1])) { + Reference* other = Reference::unwrap(env, argv[1]); + if (other != nullptr && other->data != nullptr) { + reference->data = other->data; + reference->ownsData = false; + } else if (other != nullptr && other->initValue != nullptr) { + size_t size = reference->type != nullptr && reference->type->type != nullptr && + reference->type->type->size > 0 + ? reference->type->type->size + : sizeof(void*); + reference->data = calloc(1, size); + reference->ownsData = true; + bool shouldFree = false; + napi_value initValue = get_ref_value(env, other->initValue); + reference->type->toNative(env, initValue, reference->data, &shouldFree, &shouldFree); + } } else { - reference->data = malloc(reference->type->type->size); + size_t size = reference->type != nullptr && reference->type->type != nullptr && + reference->type->type->size > 0 + ? reference->type->type->size + : sizeof(void*); + reference->data = malloc(size); reference->ownsData = true; bool shouldFree; reference->type->toNative(env, argv[1], reference->data, &shouldFree, &shouldFree); } - } else if (argc == 0) { - // reference->data = malloc(sizeof(void*)); - // const char * typestr = "@"; - // reference->type = TypeConv::Make(env, &typestr); - // reference->initValue = nullptr; } else { napi_throw_error(env, nullptr, "Invalid number of arguments"); return nullptr; @@ -835,7 +1606,7 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { napi_wrap(env, jsThis, reference, Reference::finalize, nullptr, nullptr); - return jsThis; + return createReferenceProxy(env, jsThis, reference); } napi_value Reference::get_value(napi_env env, napi_callback_info info) { @@ -844,15 +1615,23 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { Reference* ref = Reference::unwrap(env, jsThis); - napi_value result; + if (ref == nullptr) { + napi_value undefined; + napi_get_undefined(env, &undefined); + return undefined; + } if (ref->data == nullptr) { - napi_get_undefined(env, &result); - } else { - result = ref->type->toJS(env, ref->data); + if (ref->initValue != nullptr) { + return get_ref_value(env, ref->initValue); + } + + napi_value undefined; + napi_get_undefined(env, &undefined); + return undefined; } - return result; + return ref->type->toJS(env, ref->data); } napi_value Reference::set_value(napi_env env, napi_callback_info info) { @@ -863,9 +1642,30 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { Reference* ref = Reference::unwrap(env, jsThis); - if (ref->data != nullptr) { - bool shouldFree; - ref->type->toNative(env, arg, ref->data, &shouldFree, &shouldFree); + if (ref == nullptr) { + return nullptr; + } + + if (ref->data == nullptr) { + if (ref->type == nullptr) { + if (ref->initValue != nullptr) { + napi_delete_reference(env, ref->initValue); + } + ref->initValue = make_ref(env, arg); + return nullptr; + } + + size_t size = ref->type->type != nullptr && ref->type->type->size > 0 ? ref->type->type->size + : sizeof(void*); + ref->data = calloc(1, size); + ref->ownsData = true; + } + + bool shouldFree = false; + ref->type->toNative(env, arg, ref->data, &shouldFree, &shouldFree); + if (ref->initValue != nullptr) { + napi_delete_reference(env, ref->initValue); + ref->initValue = nullptr; } return nullptr; @@ -878,10 +1678,11 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { Reference* ref = Reference::unwrap(env, jsThis); napi_value result; - char* hex = (char*)malloc(17); - snprintf(hex, 17, "%0016llx", (uint64_t)ref->data); - std::string str = ""; - free(hex); + if (ref == nullptr) { + napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &result); + return result; + } + std::string str = "data) + ">"; napi_create_string_utf8(env, str.c_str(), NAPI_AUTO_LENGTH, &result); return result; @@ -905,16 +1706,73 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { napi_value FunctionReference::defineJSClass(napi_env env) { napi_value constructor; - napi_define_class(env, "FunctionReference", NAPI_AUTO_LENGTH, FunctionReference::constructor, - nullptr, 0, nullptr, &constructor); + napi_create_function(env, "FunctionReference", NAPI_AUTO_LENGTH, FunctionReference::constructor, + nullptr, &constructor); + + napi_value symbolSizeof = jsSymbolFor(env, "sizeof"); + napi_value sizeValue; + napi_create_int32(env, sizeof(void*), &sizeValue); + napi_set_property(env, constructor, symbolSizeof, sizeValue); + + napi_value prototype; + napi_get_named_property(env, constructor, "prototype", &prototype); + napi_set_property(env, prototype, symbolSizeof, sizeValue); return constructor; } +bool FunctionReference::isInstance(napi_env env, napi_value value) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType != napi_object && valueType != napi_function) { + return false; + } + + ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); + bool isInstance = false; + napi_value FunctionReferenceClass = get_ref_value(env, bridgeState->functionReferenceClass); + napi_instanceof(env, value, FunctionReferenceClass, &isInstance); + if (isInstance) { + return true; + } + + bool hasMarker = false; + napi_has_named_property(env, value, kFunctionReferenceMarker, &hasMarker); + if (!hasMarker) { + return false; + } + + napi_value marker; + napi_get_named_property(env, value, kFunctionReferenceMarker, &marker); + bool markerValue = false; + napi_get_value_bool(env, marker, &markerValue); + return markerValue; +} + FunctionReference* FunctionReference::unwrap(napi_env env, napi_value value) { FunctionReference* ref = nullptr; - napi_unwrap(env, value, (void**)&ref); - return ref; + if (napi_unwrap(env, value, (void**)&ref) == napi_ok && ref != nullptr) { + return ref; + } + + bool hasNativeRef = false; + napi_has_named_property(env, value, kFunctionReferenceDataProperty, &hasNativeRef); + if (!hasNativeRef) { + return nullptr; + } + + napi_value nativeRefValue; + if (napi_get_named_property(env, value, kFunctionReferenceDataProperty, &nativeRefValue) != + napi_ok) { + return nullptr; + } + + void* nativeRef = nullptr; + if (napi_get_value_external(env, nativeRefValue, &nativeRef) != napi_ok) { + return nullptr; + } + + return static_cast(nativeRef); } void FunctionReference::finalize(napi_env env, void* data, void* hint) { @@ -925,15 +1783,67 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { napi_value FunctionReference::constructor(napi_env env, napi_callback_info info) { napi_value jsThis; size_t argc = 1; + napi_value argv[1]; + napi_value newTarget = nullptr; + napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr); + napi_get_new_target(env, info, &newTarget); + napi_value arg; - napi_get_cb_info(env, info, &argc, &arg, &jsThis, nullptr); + if (argc == 0) { + napi_throw_type_error(env, nullptr, "FunctionReference constructor expects a function"); + return nullptr; + } + arg = argv[0]; + + napi_valuetype argType = napi_undefined; + napi_typeof(env, arg, &argType); + if (argType != napi_function) { + napi_throw_type_error(env, nullptr, "FunctionReference constructor expects a function"); + return nullptr; + } + + if (FunctionReference::isInstance(env, arg)) { + return arg; + } FunctionReference* reference = new FunctionReference(env, make_ref(env, arg)); + napi_value nativeRef; + napi_create_external(env, reference, nullptr, nullptr, &nativeRef); + napi_property_descriptor nativeRefProp = { + .utf8name = kFunctionReferenceDataProperty, + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = nativeRef, + .attributes = napi_default, + .data = nullptr, + }; + napi_define_properties(env, arg, 1, &nativeRefProp); + napi_ref finalizerRef = nullptr; + napi_add_finalizer(env, arg, reference, FunctionReference::finalize, nullptr, &finalizerRef); + + napi_value marker; + napi_get_boolean(env, true, &marker); + napi_property_descriptor markerProp = { + .utf8name = kFunctionReferenceMarker, + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = marker, + .attributes = napi_default, + .data = nullptr, + }; + napi_define_properties(env, arg, 1, &markerProp); - napi_ref ref; - napi_wrap(env, jsThis, reference, FunctionReference::finalize, nullptr, &ref); + // Keep function prototype chain untouched to avoid side effects on JS function + // semantics; expose sizeof directly on the function instance instead. + napi_value symbolSizeof = jsSymbolFor(env, "sizeof"); + napi_value sizeValue; + napi_create_int32(env, sizeof(void*), &sizeValue); + napi_set_property(env, arg, symbolSizeof, sizeValue); - return jsThis; + (void)newTarget; + return arg; } FunctionReference::~FunctionReference() { @@ -948,9 +1858,10 @@ napi_value interop_bufferFromData(napi_env env, napi_callback_info info) { } } -void* FunctionReference::getFunctionPointer(MDSectionOffset offset) { +void* FunctionReference::getFunctionPointer(MDSectionOffset offset, bool isBlock) { if (closure == nullptr) { - closure = std::make_shared(ObjCBridgeState::InstanceData(env)->metadata, offset, true); + closure = + std::make_shared(ObjCBridgeState::InstanceData(env)->metadata, offset, isBlock); closure->env = env; closure->func = ref; } diff --git a/NativeScript/ffi/JSObject.mm b/NativeScript/ffi/JSObject.mm index c54dd247..d3f16fc7 100644 --- a/NativeScript/ffi/JSObject.mm +++ b/NativeScript/ffi/JSObject.mm @@ -4,11 +4,6 @@ #import -void JSObject_finalize(napi_env, void* data, void*) { - id obj = (id)data; - [obj release]; -} - @interface JSObject : NSObject { napi_env env; napi_ref ref; @@ -16,10 +11,17 @@ @interface JSObject : NSObject { } - (instancetype)initWithEnv:(napi_env)env value:(napi_value)value; +- (void)removeFromBridgeState; - (napi_value)value; @end +void JSObject_finalize(napi_env, void* data, void*) { + JSObject* obj = (JSObject*)data; + [obj removeFromBridgeState]; + [obj release]; +} + @implementation JSObject - (instancetype)initWithEnv:(napi_env)_env value:(napi_value)value { @@ -30,10 +32,21 @@ - (instancetype)initWithEnv:(napi_env)_env value:(napi_value)value { napi_reference_ref(env, ref, &result); napi_wrap(env, value, self, nullptr, nullptr, nullptr); bridgeState = nativescript::ObjCBridgeState::InstanceData(env); - bridgeState->objectRefs[self] = ref; + if (bridgeState != nullptr) { + bridgeState->objectRefs[self] = ref; + } return self; } +- (void)removeFromBridgeState { + if (bridgeState == nullptr) { + return; + } + + bridgeState->objectRefs.erase(self); + bridgeState = nullptr; +} + - (napi_value)value { napi_value result; napi_get_reference_value(env, ref, &result); @@ -42,7 +55,6 @@ - (napi_value)value { - (void)dealloc { napi_delete_reference(env, ref); - bridgeState->objectRefs.erase(self); [super dealloc]; } diff --git a/NativeScript/ffi/ObjCBridge.h b/NativeScript/ffi/ObjCBridge.h index 7261450a..062d06c9 100644 --- a/NativeScript/ffi/ObjCBridge.h +++ b/NativeScript/ffi/ObjCBridge.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "AutoreleasePool.h" #include "CFunction.h" @@ -28,7 +29,11 @@ using namespace metagen; namespace nativescript { +class ObjCBridgeState; + void finalize_objc_object(napi_env /*env*/, void* data, void* hint); +bool IsBridgeStateLive(const ObjCBridgeState* bridgeState, + uint64_t token) noexcept; // Determines how retain/release should be called when an Objective-C // object is exposed to JavaScript land. @@ -57,6 +62,17 @@ class ObjCBridgeState { return bridgeState; } + static inline uintptr_t NormalizeHandleKey(void* handle) { + if (handle == nullptr) { + return 0; + } +#if INTPTR_MAX == INT64_MAX + return reinterpret_cast(handle) & 0x0000FFFFFFFFFFFFULL; +#else + return reinterpret_cast(handle); +#endif + } + void registerVarGlobals(napi_env env, napi_value global); void registerEnumGlobals(napi_env env, napi_value global); void registerStructGlobals(napi_env env, napi_value global); @@ -83,9 +99,145 @@ class ObjCBridgeState { ObjectOwnership ownership = kUnownedObject, MDSectionOffset classOffset = 0, std::vector* protocolOffsets = nullptr); + napi_value findCachedObjectWrapper(napi_env env, id object); + inline void cacheHandleObject(napi_env env, void* handle, napi_value value) { + if (handle == nullptr || value == nullptr) { + return; + } + + uintptr_t handleKey = NormalizeHandleKey(handle); + auto it = handleObjectRefs.find(handleKey); + if (it != handleObjectRefs.end()) { + auto existing = get_ref_value(env, it->second); + if (existing != nullptr) { + bool isSameValue = false; + if (napi_strict_equals(env, existing, value, &isSameValue) == napi_ok && + isSameValue) { + return; + } + } + + napi_delete_reference(env, it->second); + handleObjectRefs.erase(it); + } + + napi_ref ref = nullptr; + napi_create_reference(env, value, 0, &ref); + handleObjectRefs[handleKey] = ref; + } + inline napi_value getCachedHandleObject(napi_env env, void* handle) { + if (handle == nullptr) { + return nullptr; + } + + uintptr_t handleKey = NormalizeHandleKey(handle); + auto it = handleObjectRefs.find(handleKey); + if (it == handleObjectRefs.end()) { + return nullptr; + } + + auto value = get_ref_value(env, it->second); + if (value == nullptr) { + napi_delete_reference(env, it->second); + handleObjectRefs.erase(it); + } + + return value; + } void unregisterObject(id object) noexcept; + inline void beginRoundTripCacheFrame(napi_env /*env*/) { + roundTripCacheFrames.emplace_back(); + } + + inline bool hasRoundTripCacheFrame() const { + return !roundTripCacheFrames.empty(); + } + + inline void cacheRoundTripObject(napi_env env, id object, napi_value value) { + if (object == nil || roundTripCacheFrames.empty()) { + return; + } + + auto& frame = roundTripCacheFrames.back(); + if (frame.find(object) != frame.end()) { + return; + } + + frame[object] = make_ref(env, value); + } + + inline napi_value getRoundTripObject(napi_env env, id object) const { + if (object == nil) { + return nullptr; + } + + if (roundTripCacheFrames.empty()) { + auto recent = recentRoundTripCache.find(object); + if (recent == recentRoundTripCache.end()) { + uintptr_t objectKey = NormalizeHandleKey((void*)object); + for (const auto& entry : recentRoundTripCache) { + if (NormalizeHandleKey((void*)entry.first) == objectKey) { + return get_ref_value(env, entry.second); + } + } + + return nullptr; + } + + return get_ref_value(env, recent->second); + } + + uintptr_t objectKey = NormalizeHandleKey((void*)object); + for (auto frame = roundTripCacheFrames.rbegin(); + frame != roundTripCacheFrames.rend(); ++frame) { + auto find = frame->find(object); + if (find != frame->end()) { + return get_ref_value(env, find->second); + } + + for (const auto& entry : *frame) { + if (NormalizeHandleKey((void*)entry.first) == objectKey) { + return get_ref_value(env, entry.second); + } + } + } + + auto recent = recentRoundTripCache.find(object); + if (recent != recentRoundTripCache.end()) { + return get_ref_value(env, recent->second); + } + + return nullptr; + } + + inline void endRoundTripCacheFrame(napi_env env) { + if (roundTripCacheFrames.empty()) { + return; + } + + auto frame = std::move(roundTripCacheFrames.back()); + roundTripCacheFrames.pop_back(); + + if (!roundTripCacheFrames.empty()) { + auto& parent = roundTripCacheFrames.back(); + for (auto& entry : frame) { + if (parent.find(entry.first) == parent.end()) { + parent[entry.first] = entry.second; + } else { + napi_delete_reference(env, entry.second); + } + } + return; + } + + for (const auto& entry : recentRoundTripCache) { + napi_delete_reference(env, entry.second); + } + recentRoundTripCache = std::move(frame); + } + CFunction* getCFunction(napi_env env, MDSectionOffset offset); inline StructInfo* getStructInfo(napi_env env, MDSectionOffset offset) { @@ -113,13 +265,17 @@ class ObjCBridgeState { } public: + napi_env env = nullptr; + uint64_t lifetimeToken = 0; std::unordered_map objectRefs; + std::unordered_map handleObjectRefs; - napi_ref pointerClass; - napi_ref referenceClass; - napi_ref createNativeProxy; - napi_ref createFastEnumeratorIterator; - napi_ref transferOwnershipToNative; + napi_ref pointerClass = nullptr; + napi_ref referenceClass = nullptr; + napi_ref functionReferenceClass = nullptr; + napi_ref createNativeProxy = nullptr; + napi_ref createFastEnumeratorIterator = nullptr; + napi_ref transferOwnershipToNative = nullptr; std::unordered_map classes; std::unordered_map protocols; @@ -143,7 +299,27 @@ class ObjCBridgeState { MDMetadataReader* metadata; private: + inline napi_value getNormalizedObjectRef(napi_env env, id object) const { + auto exact = objectRefs.find(object); + if (exact != objectRefs.end()) { + return get_ref_value(env, exact->second); + } + + uintptr_t objectKey = NormalizeHandleKey((void*)object); + for (const auto& entry : objectRefs) { + if (NormalizeHandleKey((void*)entry.first) != objectKey) { + continue; + } + + return get_ref_value(env, entry.second); + } + + return nullptr; + } + std::unordered_map structInfoCache; + std::vector> roundTripCacheFrames; + std::unordered_map recentRoundTripCache; void* objc_autoreleasePool; }; diff --git a/NativeScript/ffi/ObjCBridge.mm b/NativeScript/ffi/ObjCBridge.mm index 140246a3..7afd7de4 100644 --- a/NativeScript/ffi/ObjCBridge.mm +++ b/NativeScript/ffi/ObjCBridge.mm @@ -13,15 +13,22 @@ #include "ObjectRef.h" #include "Struct.h" #include "TypeConv.h" +#include "Util.h" #include "Variable.h" #include "js_native_api.h" #include "js_native_api_types.h" #include "node_api_util.h" #import +#include +#include #include #include #include +#include +#include +#include +#include #ifdef EMBED_METADATA_SIZE const unsigned char __attribute__((section("__objc_metadata,__objc_metadata"))) @@ -33,6 +40,41 @@ const unsigned char __attribute__((section("__objc_metadata,__objc_metadata"))) #endif namespace nativescript { +namespace { +std::mutex gLiveBridgeStatesMutex; +std::unordered_map gLiveBridgeStates; +std::atomic gNextBridgeStateToken{1}; + +uint64_t RegisterBridgeState(const ObjCBridgeState* bridgeState) { + if (bridgeState == nullptr) { + return 0; + } + + uint64_t token = gNextBridgeStateToken.fetch_add(1, std::memory_order_relaxed); + std::lock_guard lock(gLiveBridgeStatesMutex); + gLiveBridgeStates[bridgeState] = token; + return token; +} + +void UnregisterBridgeState(const ObjCBridgeState* bridgeState) { + if (bridgeState == nullptr) { + return; + } + + std::lock_guard lock(gLiveBridgeStatesMutex); + gLiveBridgeStates.erase(bridgeState); +} +} // namespace + +bool IsBridgeStateLive(const ObjCBridgeState* bridgeState, uint64_t token) noexcept { + if (bridgeState == nullptr || token == 0) { + return false; + } + + std::lock_guard lock(gLiveBridgeStatesMutex); + auto find = gLiveBridgeStates.find(bridgeState); + return find != gLiveBridgeStates.end() && find->second == token; +} void finalize_bridge_data(napi_env env, void* data, void* hint) { auto bridgeState = (ObjCBridgeState*)data; @@ -58,27 +100,600 @@ void finalize_bridge_data(napi_env env, void* data, void* hint) { return new MDMetadataReader(buffer); } +inline bool hasNamedProperty(napi_env env, napi_value object, const char* name) { + bool hasProperty = false; + napi_has_named_property(env, object, name, &hasProperty); + return hasProperty; +} + +inline bool isFunctionValue(napi_env env, napi_value value) { + if (value == nullptr) { + return false; + } + napi_valuetype valueType = napi_undefined; + if (napi_typeof(env, value, &valueType) != napi_ok) { + return false; + } + return valueType == napi_function; +} + +inline void clearPendingException(napi_env env) { + bool hasPendingException = false; + if (napi_is_exception_pending(env, &hasPendingException) == napi_ok && hasPendingException) { + napi_value exception = nullptr; + napi_get_and_clear_last_exception(env, &exception); + } +} + +inline bool isConstructableValue(napi_env env, napi_value value) { + if (!isFunctionValue(env, value)) { + return false; + } + + napi_value instance = nullptr; + napi_status status = napi_new_instance(env, value, 0, nullptr, &instance); + if (status == napi_ok) { + return true; + } + + clearPendingException(env); + return false; +} + +inline bool hasConstructableNamedProperty(napi_env env, napi_value global, const char* name) { + if (!hasNamedProperty(env, global, name)) { + return false; + } + + napi_value value = nullptr; + if (napi_get_named_property(env, global, name, &value) != napi_ok || value == nullptr) { + clearPendingException(env); + return false; + } + + return isConstructableValue(env, value); +} + +inline void defineGlobalValue(napi_env env, napi_value global, const char* name, napi_value value) { + if (name == nullptr || value == nullptr) { + return; + } + + if (napi_set_named_property(env, global, name, value) == napi_ok) { + return; + } + clearPendingException(env); + + napi_property_descriptor prop = { + .utf8name = name, + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = value, + .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), + .data = nullptr, + }; + if (napi_define_properties(env, global, 1, &prop) != napi_ok) { + clearPendingException(env); + } +} + +inline bool defineConstructableGlobalValue(napi_env env, napi_value global, const char* name, + napi_value value) { + if (!isConstructableValue(env, value)) { + return false; + } + + if (napi_set_named_property(env, global, name, value) == napi_ok && + hasConstructableNamedProperty(env, global, name)) { + return true; + } + clearPendingException(env); + + napi_property_descriptor prop = { + .utf8name = name, + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = value, + .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), + .data = nullptr, + }; + if (napi_define_properties(env, global, 1, &prop) == napi_ok && + hasConstructableNamedProperty(env, global, name)) { + return true; + } + clearPendingException(env); + + napi_value key = nullptr; + napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &key); + if (key != nullptr) { + bool deleted = false; + if (napi_delete_property(env, global, key, &deleted) == napi_ok && deleted) { + if (napi_define_properties(env, global, 1, &prop) == napi_ok && + hasConstructableNamedProperty(env, global, name)) { + return true; + } + clearPendingException(env); + if (napi_set_named_property(env, global, name, value) == napi_ok && + hasConstructableNamedProperty(env, global, name)) { + return true; + } + clearPendingException(env); + } else { + clearPendingException(env); + } + } + + return hasConstructableNamedProperty(env, global, name); +} + +inline std::string buildStructEncoding(StructInfo* info) { + if (info == nullptr || info->name == nullptr) { + return ""; + } + + std::string encoding = "{"; + encoding += info->name; + encoding += "="; + for (const auto& field : info->fields) { + if (field.type == nullptr) { + return ""; + } + field.type->encode(&encoding); + } + encoding += "}"; + return encoding; +} + +inline void setTypeEncodingSymbol(napi_env env, napi_value value, const std::string& encoding) { + if (value == nullptr || encoding.empty()) { + return; + } + + napi_value typeSymbol = jsSymbolFor(env, "type"); + napi_value encodedValue = nullptr; + napi_create_string_utf8(env, encoding.c_str(), NAPI_AUTO_LENGTH, &encodedValue); + if (typeSymbol != nullptr && encodedValue != nullptr) { + napi_set_property(env, value, typeSymbol, encodedValue); + } +} + +inline void registerStructAlias(napi_env env, napi_value global, ObjCBridgeState* bridgeState, + const char* aliasName, + std::initializer_list candidates) { + if (bridgeState == nullptr || aliasName == nullptr) { + return; + } + + if (hasNamedProperty(env, global, aliasName)) { + napi_value existing = nullptr; + if (napi_get_named_property(env, global, aliasName, &existing) == napi_ok && + isFunctionValue(env, existing)) { + return; + } + } + + for (const char* candidate : candidates) { + if (candidate == nullptr || candidate[0] == '\0') { + continue; + } + + auto structIt = bridgeState->structOffsets.find(candidate); + if (structIt != bridgeState->structOffsets.end()) { + StructInfo* info = bridgeState->getStructInfo(env, structIt->second); + if (info != nullptr) { + napi_value cls = StructObject::getJSClass(env, info); + if (isFunctionValue(env, cls)) { + setTypeEncodingSymbol(env, cls, buildStructEncoding(info)); + defineGlobalValue(env, global, aliasName, cls); + return; + } + } + } + + if (hasNamedProperty(env, global, candidate)) { + napi_value source = nullptr; + if (napi_get_named_property(env, global, candidate, &source) == napi_ok && + isFunctionValue(env, source)) { + defineGlobalValue(env, global, aliasName, source); + return; + } + } + } +} + +inline void ensureSyntheticCGPoint(napi_env env, napi_value global) { + if (hasConstructableNamedProperty(env, global, "CGPoint")) { + return; + } + + static StructInfo* syntheticInfo = nullptr; + if (syntheticInfo == nullptr) { + syntheticInfo = new StructInfo(); + syntheticInfo->name = strdup("CGPoint"); + syntheticInfo->size = sizeof(double) * 2; + syntheticInfo->jsClass = nullptr; + + const char* doubleEncodingX = "d"; + const char* doubleEncodingY = "d"; + + StructFieldInfo fieldX; + fieldX.name = strdup("x"); + fieldX.offset = 0; + fieldX.type = TypeConv::Make(env, &doubleEncodingX); + syntheticInfo->fields.push_back(fieldX); + + StructFieldInfo fieldY; + fieldY.name = strdup("y"); + fieldY.offset = sizeof(double); + fieldY.type = TypeConv::Make(env, &doubleEncodingY); + syntheticInfo->fields.push_back(fieldY); + } + + napi_value cls = StructObject::getJSClass(env, syntheticInfo); + if (!isFunctionValue(env, cls)) { + return; + } + + setTypeEncodingSymbol(env, cls, "{CGPoint=dd}"); + defineConstructableGlobalValue(env, global, "CGPoint", cls); +} + +inline void ensureConstructableStructAlias(napi_env env, napi_value global, + ObjCBridgeState* bridgeState, const char* aliasName, + std::initializer_list candidates) { + if (bridgeState == nullptr || aliasName == nullptr) { + return; + } + + if (hasConstructableNamedProperty(env, global, aliasName)) { + return; + } + + for (const char* candidate : candidates) { + if (candidate == nullptr || candidate[0] == '\0') { + continue; + } + + if (hasNamedProperty(env, global, candidate)) { + napi_value value = nullptr; + if (napi_get_named_property(env, global, candidate, &value) == napi_ok && + defineConstructableGlobalValue(env, global, aliasName, value)) { + return; + } + clearPendingException(env); + } + + auto structIt = bridgeState->structOffsets.find(candidate); + if (structIt != bridgeState->structOffsets.end()) { + StructInfo* info = bridgeState->getStructInfo(env, structIt->second); + if (info != nullptr) { + napi_value cls = StructObject::getJSClass(env, info); + if (defineConstructableGlobalValue(env, global, aliasName, cls)) { + setTypeEncodingSymbol(env, cls, buildStructEncoding(info)); + return; + } + } + } + } +} + +inline void installMacUIColorCompatShim(napi_env env) { + const char* script = R"( + (function (globalObject) { + if (typeof globalObject.UIColor === "undefined" && + typeof globalObject.NSColor === "function") { + globalObject.UIColor = globalObject.NSColor; + } + + const colorCtor = globalObject.UIColor || globalObject.NSColor; + if (typeof colorCtor !== "function" || !colorCtor.prototype) { + return; + } + + if (typeof colorCtor.prototype.initWithRedGreenBlueAlpha === "function") { + return; + } + + colorCtor.prototype.initWithRedGreenBlueAlpha = function (red, green, blue, alpha) { + if (typeof this.initWithSRGBRedGreenBlueAlpha === "function") { + return this.initWithSRGBRedGreenBlueAlpha(red, green, blue, alpha); + } + if (typeof this.initWithCalibratedRedGreenBlueAlpha === "function") { + return this.initWithCalibratedRedGreenBlueAlpha(red, green, blue, alpha); + } + if (typeof colorCtor.colorWithSRGBRedGreenBlueAlpha === "function") { + return colorCtor.colorWithSRGBRedGreenBlueAlpha(red, green, blue, alpha); + } + if (typeof colorCtor.colorWithCalibratedRedGreenBlueAlpha === "function") { + return colorCtor.colorWithCalibratedRedGreenBlueAlpha(red, green, blue, alpha); + } + return this; + }; + })(globalThis); + )"; + + napi_value shim = nullptr; + napi_create_string_utf8(env, script, NAPI_AUTO_LENGTH, &shim); + if (shim != nullptr) { + napi_value result = nullptr; + napi_run_script(env, shim, &result); + } +} + +inline void* resolveSymbolPointer(ObjCBridgeState* bridgeState, const char* symbolName) { + if (bridgeState == nullptr || symbolName == nullptr || symbolName[0] == '\0') { + return nullptr; + } + + void* symbol = dlsym(bridgeState->self_dl, symbolName); + if (symbol == nullptr) { + symbol = dlsym(RTLD_DEFAULT, symbolName); + } + if (symbol == nullptr) { + std::string underscored = "_"; + underscored += symbolName; + symbol = dlsym(bridgeState->self_dl, underscored.c_str()); + if (symbol == nullptr) { + symbol = dlsym(RTLD_DEFAULT, underscored.c_str()); + } + } + + return symbol; +} + +inline bool unwrapCompatNativeHandle(napi_env env, napi_value value, void** out) { + if (value == nullptr || out == nullptr) { + return false; + } + + if (Pointer::isInstance(env, value)) { + Pointer* ptr = Pointer::unwrap(env, value); + *out = ptr != nullptr ? ptr->data : nullptr; + return ptr != nullptr; + } + + if (Reference::isInstance(env, value)) { + Reference* ref = Reference::unwrap(env, value); + *out = ref != nullptr ? ref->data : nullptr; + return ref != nullptr; + } + + napi_valuetype valueType = napi_undefined; + if (napi_typeof(env, value, &valueType) != napi_ok) { + return false; + } + + if (valueType == napi_bigint) { + uint64_t raw = 0; + bool lossless = false; + if (napi_get_value_bigint_uint64(env, value, &raw, &lossless) != napi_ok) { + return false; + } + *out = reinterpret_cast(static_cast(raw)); + return true; + } + + if (valueType == napi_external) { + return napi_get_value_external(env, value, out) == napi_ok; + } + + if (valueType != napi_object && valueType != napi_function) { + return false; + } + + bool hasNativePointer = false; + if (napi_has_named_property(env, value, "__ns_native_ptr", &hasNativePointer) == napi_ok && + hasNativePointer) { + napi_value nativePointerValue = nullptr; + if (napi_get_named_property(env, value, "__ns_native_ptr", &nativePointerValue) == napi_ok && + napi_get_value_external(env, nativePointerValue, out) == napi_ok && *out != nullptr) { + return true; + } + } + + return napi_unwrap(env, value, out) == napi_ok && *out != nullptr; +} + +inline napi_value createCompatDispatchQueueWrapper(napi_env env, dispatch_queue_t queue) { + if (queue == nullptr) { + napi_value nullValue = nullptr; + napi_get_null(env, &nullValue); + return nullValue; + } + + return Pointer::create(env, reinterpret_cast(queue)); +} + +inline napi_value compat_dispatch_get_global_queue(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value argv[2] = {nullptr, nullptr}; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + int64_t identifier = 0; + if (argc > 0) { + napi_valuetype identifierType = napi_undefined; + if (napi_typeof(env, argv[0], &identifierType) == napi_ok && identifierType == napi_bigint) { + bool lossless = false; + if (napi_get_value_bigint_int64(env, argv[0], &identifier, &lossless) != napi_ok) { + napi_throw_type_error(env, nullptr, + "dispatch_get_global_queue expects a numeric identifier."); + return nullptr; + } + } else { + napi_value coercedIdentifier = nullptr; + if (napi_coerce_to_number(env, argv[0], &coercedIdentifier) != napi_ok || + napi_get_value_int64(env, coercedIdentifier, &identifier) != napi_ok) { + napi_throw_type_error(env, nullptr, + "dispatch_get_global_queue expects a numeric identifier."); + return nullptr; + } + } + } + + uint64_t flags = 0; + if (argc > 1) { + napi_valuetype flagsType = napi_undefined; + if (napi_typeof(env, argv[1], &flagsType) == napi_ok && flagsType == napi_bigint) { + bool lossless = false; + if (napi_get_value_bigint_uint64(env, argv[1], &flags, &lossless) != napi_ok) { + napi_throw_type_error(env, nullptr, "dispatch_get_global_queue expects numeric flags."); + return nullptr; + } + } else { + napi_value coercedFlags = nullptr; + int64_t signedFlags = 0; + if (napi_coerce_to_number(env, argv[1], &coercedFlags) != napi_ok || + napi_get_value_int64(env, coercedFlags, &signedFlags) != napi_ok) { + napi_throw_type_error(env, nullptr, "dispatch_get_global_queue expects numeric flags."); + return nullptr; + } + flags = static_cast(signedFlags); + } + } + + return createCompatDispatchQueueWrapper(env, dispatch_get_global_queue(identifier, flags)); +} + +inline napi_value compat_dispatch_get_current_queue(napi_env env, napi_callback_info info) { + (void)info; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return createCompatDispatchQueueWrapper(env, dispatch_get_current_queue()); +#pragma clang diagnostic pop +} + +inline napi_value compat_dispatch_async(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value argv[2] = {nullptr, nullptr}; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + if (argc < 2) { + napi_throw_type_error(env, nullptr, "dispatch_async expects a queue and callback."); + return nullptr; + } + + void* queueHandle = nullptr; + if (!unwrapCompatNativeHandle(env, argv[0], &queueHandle) || queueHandle == nullptr) { + napi_throw_type_error(env, nullptr, "dispatch_async expects a native queue handle."); + return nullptr; + } + + napi_valuetype callbackType = napi_undefined; + if (napi_typeof(env, argv[1], &callbackType) != napi_ok || callbackType != napi_function) { + napi_throw_type_error(env, nullptr, "dispatch_async expects a function callback."); + return nullptr; + } + + auto closure = new Closure(std::string("v"), true); + closure->env = env; + id block = registerBlock(env, closure, argv[1]); + dispatch_block_t dispatchBlock = (dispatch_block_t)block; + + dispatch_async(reinterpret_cast(queueHandle), dispatchBlock); + [block release]; + + napi_value undefinedValue = nullptr; + napi_get_undefined(env, &undefinedValue); + return undefinedValue; +} + +inline void registerCompatFunctionIfMissing(napi_env env, napi_value global, + ObjCBridgeState* bridgeState, const char* functionName, + const char* encoding) { + if (hasNamedProperty(env, global, functionName)) { + return; + } + + void* fn = resolveSymbolPointer(bridgeState, functionName); + if (fn == nullptr && strcmp(functionName, "CC_SHA256") == 0) { + void* commonCrypto = dlopen("/usr/lib/system/libcommonCrypto.dylib", RTLD_NOW | RTLD_LOCAL); + if (commonCrypto != nullptr) { + fn = dlsym(commonCrypto, functionName); + if (fn == nullptr) { + fn = dlsym(commonCrypto, "_CC_SHA256"); + } + } + } + + if (fn == nullptr) { + return; + } + + napi_value wrapper = FunctionPointer::wrapWithEncoding(env, fn, encoding, false); + if (wrapper != nullptr) { + napi_set_named_property(env, global, functionName, wrapper); + } +} + +inline void registerCompatFunction(napi_env env, napi_value global, const char* functionName, + napi_callback callback) { + napi_value wrapper = nullptr; + napi_create_function(env, functionName, NAPI_AUTO_LENGTH, callback, nullptr, &wrapper); + if (wrapper != nullptr) { + napi_value key = nullptr; + napi_create_string_utf8(env, functionName, NAPI_AUTO_LENGTH, &key); + if (key != nullptr) { + bool deleted = false; + napi_delete_property(env, global, key, &deleted); + clearPendingException(env); + } + defineGlobalValue(env, global, functionName, wrapper); + } +} + +void registerLegacyCompatGlobals(napi_env env, napi_value global, ObjCBridgeState* bridgeState) { +#if TARGET_OS_OSX + registerStructAlias(env, global, bridgeState, "CGPoint", + {"CGPoint", "_CGPoint", "NSPoint", "_NSPoint"}); + registerStructAlias(env, global, bridgeState, "CGSize", + {"CGSize", "_CGSize", "NSSize", "_NSSize"}); + registerStructAlias(env, global, bridgeState, "CGRect", + {"CGRect", "_CGRect", "NSRect", "_NSRect"}); + ensureSyntheticCGPoint(env, global); + ensureConstructableStructAlias( + env, global, bridgeState, "CGPoint", + {"CGPointStruct", "NSPoint", "NSPointStruct", "_CGPoint", "_NSPoint", "CGPoint"}); + installMacUIColorCompatShim(env); +#endif + + // CommonCrypto compatibility used by historical runtime tests and apps. + registerCompatFunctionIfMissing(env, global, bridgeState, "CC_SHA256", "^C^vQ^C"); + registerCompatFunctionIfMissing(env, global, bridgeState, "CGColorGetComponents", "^d^v"); + + // Force known-good libdispatch globals on macOS. The metadata path can resolve these with an + // incompatible call shape, which crashes when tests dispatch timers from a background queue. + registerCompatFunction(env, global, "dispatch_async", compat_dispatch_async); + registerCompatFunction(env, global, "dispatch_get_current_queue", + compat_dispatch_get_current_queue); + registerCompatFunction(env, global, "dispatch_get_global_queue", + compat_dispatch_get_global_queue); +} + ObjCBridgeState::ObjCBridgeState(napi_env env, const char* metadata_path, const void* metadata_ptr) { + this->env = env; napi_set_instance_data(env, this, finalize_bridge_data, nil); + lifetimeToken = RegisterBridgeState(this); self_dl = dlopen(nullptr, RTLD_NOW); if (metadata_ptr && *((const char*)metadata_ptr) != '\0') { - #ifdef EMBED_METADATA_SIZE - NSLog(@"Ignoring metadata pointer due to embedded metadata"); +#ifdef EMBED_METADATA_SIZE + // NSLog(@"Ignoring metadata pointer due to embedded metadata"); metadata = new MDMetadataReader((void*)embedded_metadata); - #else - NSLog(@"Using metadata from pointer: %p", metadata_ptr); +#else + // NSLog(@"Using metadata from pointer: %p", metadata_ptr); metadata = new MDMetadataReader((void*)metadata_ptr); - #endif +#endif } else { #ifdef EMBED_METADATA_SIZE if (metadata_path != nullptr) { - NSLog(@"Loading metadata from file: %s", metadata_path); + // NSLog(@"Loading metadata from file: %s", metadata_path); metadata = loadMetadataFromFile(metadata_path); } else { - NSLog(@"Using embedded metadata"); + // NSLog(@"Using embedded metadata"); metadata = new MDMetadataReader((void*)embedded_metadata); } #else @@ -97,6 +712,65 @@ void finalize_bridge_data(napi_env env, void* data, void* hint) { } ObjCBridgeState::~ObjCBridgeState() { + UnregisterBridgeState(this); + + auto deleteRef = [&](napi_ref& ref) { + if (env != nullptr && ref != nullptr) { + napi_delete_reference(env, ref); + ref = nullptr; + } + }; + + for (auto& pair : constructorsByPointer) { + deleteRef(pair.second); + } + constructorsByPointer.clear(); + + for (auto& frame : roundTripCacheFrames) { + for (auto& entry : frame) { + deleteRef(entry.second); + } + } + roundTripCacheFrames.clear(); + + for (auto& entry : recentRoundTripCache) { + deleteRef(entry.second); + } + recentRoundTripCache.clear(); + + for (auto& entry : handleObjectRefs) { + deleteRef(entry.second); + } + handleObjectRefs.clear(); + + std::unordered_set classAndProtocolConstructorRefs; + classAndProtocolConstructorRefs.reserve(classes.size() + protocols.size()); + for (const auto& pair : classes) { + if (pair.second != nullptr && pair.second->constructor != nullptr) { + classAndProtocolConstructorRefs.insert(pair.second->constructor); + } + } + for (const auto& pair : protocols) { + if (pair.second != nullptr && pair.second->constructor != nullptr) { + classAndProtocolConstructorRefs.insert(pair.second->constructor); + } + } + for (auto& pair : mdValueCache) { + napi_ref& ref = pair.second; + if (ref != nullptr && + classAndProtocolConstructorRefs.find(ref) == classAndProtocolConstructorRefs.end()) { + deleteRef(ref); + } + } + mdValueCache.clear(); + + deleteRef(pointerClass); + deleteRef(referenceClass); + deleteRef(functionReferenceClass); + deleteRef(createNativeProxy); + deleteRef(createFastEnumeratorIterator); + deleteRef(transferOwnershipToNative); + // Clean up cached Cif objects for (auto& pair : cifs) { delete pair.second; @@ -171,6 +845,7 @@ void finalize_bridge_data(napi_env env, void* data, void* hint) { } objectRefs[nativeObject] = ref; + attachObjectLifecycleAssociation(env, nativeObject); return result; } @@ -278,4 +953,5 @@ NAPI_EXPORT void nativescript_init(void* _env, const char* metadata_path, bridgeState->registerFunctionGlobals(env, global); bridgeState->registerClassGlobals(env, global); bridgeState->registerProtocolGlobals(env, global); + registerLegacyCompatGlobals(env, global, bridgeState); } diff --git a/NativeScript/ffi/Object.h b/NativeScript/ffi/Object.h index c06fcac9..2290468f 100644 --- a/NativeScript/ffi/Object.h +++ b/NativeScript/ffi/Object.h @@ -6,6 +6,7 @@ namespace nativescript { void initProxyFactory(napi_env env, ObjCBridgeState* bridgeState); +void attachObjectLifecycleAssociation(napi_env env, id object); } // namespace nativescript diff --git a/NativeScript/ffi/Object.mm b/NativeScript/ffi/Object.mm index 9bbd6fb8..04756897 100644 --- a/NativeScript/ffi/Object.mm +++ b/NativeScript/ffi/Object.mm @@ -1,13 +1,16 @@ #include "Object.h" +#include #include "JSObject.h" #include "ObjCBridge.h" #include "js_native_api.h" #include "node_api_util.h" #import +#import #include static SEL JSWrapperObjectAssociationKey = @selector(JSWrapperObjectAssociationKey); +static SEL ObjCLifecycleAssociationKey = @selector(ObjCLifecycleAssociationKey); @interface JSWrapperObjectAssociation : NSObject @@ -22,6 +25,16 @@ - (instancetype)initWithEnv:(napi_env)env ref:(napi_ref)ref; @end +@interface ObjCLifecycleAssociation : NSObject { + nativescript::ObjCBridgeState* _bridgeState; + uint64_t _bridgeStateToken; + uintptr_t _objectAddress; +} + +- (instancetype)initWithBridgeState:(nativescript::ObjCBridgeState*)bridgeState object:(id)object; + +@end + @implementation JSWrapperObjectAssociation - (instancetype)initWithEnv:(napi_env)env ref:(napi_ref)ref { @@ -52,6 +65,34 @@ - (void)dealloc { @end +@implementation ObjCLifecycleAssociation + +- (instancetype)initWithBridgeState:(nativescript::ObjCBridgeState*)bridgeState object:(id)object { + self = [super init]; + if (self) { + _bridgeState = bridgeState; + _bridgeStateToken = bridgeState != nullptr ? bridgeState->lifetimeToken : 0; + _objectAddress = (uintptr_t)object; + } + + return self; +} + +- (void)dealloc { + nativescript::ObjCBridgeState* bridgeState = _bridgeState; + uint64_t bridgeStateToken = _bridgeStateToken; + uintptr_t objectAddress = _objectAddress; + dispatch_async(dispatch_get_main_queue(), ^{ + if (nativescript::IsBridgeStateLive(bridgeState, bridgeStateToken)) { + bridgeState->objectRefs.erase((id)objectAddress); + } + }); + + [super dealloc]; +} + +@end + napi_value JS_transferOwnershipToNative(napi_env env, napi_callback_info cbinfo) { size_t argc = 1; napi_value arg; @@ -119,6 +160,27 @@ void initProxyFactory(napi_env env, ObjCBridgeState* state) { state->transferOwnershipToNative = make_ref(env, transferOwnershipToNative); } +void attachObjectLifecycleAssociation(napi_env env, id object) { + if (object == nil) { + return; + } + + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState == nullptr) { + return; + } + + if (objc_getAssociatedObject(object, ObjCLifecycleAssociationKey) != nil) { + return; + } + + ObjCLifecycleAssociation* association = + [[ObjCLifecycleAssociation alloc] initWithBridgeState:bridgeState object:object]; + objc_setAssociatedObject(object, ObjCLifecycleAssociationKey, association, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + [association release]; +} + void finalize_objc_object(napi_env /*env*/, void* data, void* hint) { id object = static_cast(data); ObjCBridgeState* bridgeState = static_cast(hint); @@ -133,21 +195,8 @@ void finalize_objc_object(napi_env /*env*/, void* data, void* hint) { NAPI_PREAMBLE - auto find = objectRefs.find(obj); - if (find != objectRefs.end()) { - auto value = get_ref_value(env, find->second); - if (value != nullptr) { - return value; - } - - unregisterObject(obj); - } - - JSWrapperObjectAssociation* association = [JSWrapperObjectAssociation associationFor:obj]; - if (association != nil) { - napi_value jsObject = get_ref_value(env, association.ref); - [obj retain]; - return proxyNativeObject(env, jsObject, obj); + if (napi_value cached = findCachedObjectWrapper(env, obj); cached != nullptr) { + return cached; } napi_value result = nil; @@ -207,6 +256,40 @@ void finalize_objc_object(napi_env /*env*/, void* data, void* hint) { return result; } +napi_value ObjCBridgeState::findCachedObjectWrapper(napi_env env, id obj) { + if (obj == nil) { + return nullptr; + } + + auto roundTrip = getRoundTripObject(env, obj); + if (roundTrip != nullptr) { + return roundTrip; + } + + if (napi_value handleCached = getCachedHandleObject(env, (void*)obj); handleCached != nullptr) { + void* wrapped = nullptr; + if (napi_unwrap(env, handleCached, &wrapped) == napi_ok && + NormalizeHandleKey(wrapped) == NormalizeHandleKey((void*)obj)) { + return handleCached; + } + } + + if (napi_value existing = getNormalizedObjectRef(env, obj); existing != nullptr) { + return existing; + } + + JSWrapperObjectAssociation* association = [JSWrapperObjectAssociation associationFor:obj]; + if (association != nil) { + napi_value jsObject = get_ref_value(env, association.ref); + if (jsObject != nullptr) { + [obj retain]; + return proxyNativeObject(env, jsObject, obj); + } + } + + return nullptr; +} + napi_value findConstructorForObject(napi_env env, ObjCBridgeState* bridgeState, id object, Class cls = nil) { if (cls == nil) { @@ -295,15 +378,13 @@ napi_value findConstructorForObject(napi_env env, ObjCBridgeState* bridgeState, return nullptr; } - auto find = objectRefs.find(obj); - if (find != objectRefs.end()) { - auto value = get_ref_value(env, find->second); - if (value != nullptr) { - return value; - } + auto roundTrip = getRoundTripObject(env, obj); + if (roundTrip != nullptr) { + return roundTrip; + } - // It was collected, but not unregistered yet. - unregisterObject(obj); + if (napi_value existing = getNormalizedObjectRef(env, obj); existing != nullptr) { + return existing; } auto findClass = classesByPointer.find(obj); diff --git a/NativeScript/ffi/Protocol.mm b/NativeScript/ffi/Protocol.mm index f1a78473..cc65e17b 100644 --- a/NativeScript/ffi/Protocol.mm +++ b/NativeScript/ffi/Protocol.mm @@ -6,10 +6,49 @@ #include "Util.h" #include "js_native_api.h" #include "node_api_util.h" +#include #import namespace nativescript { +namespace { +Protocol* resolveRuntimeProtocol(const char* metadataProtocolName) { + if (metadataProtocolName == nullptr) { + return nil; + } + + Protocol* protocol = objc_getProtocol(metadataProtocolName); + if (protocol != nil) { + return protocol; + } + + static const std::string suffix = "Protocol"; + std::string name(metadataProtocolName); + size_t suffixPos = name.rfind(suffix); + if (suffixPos == std::string::npos) { + return nil; + } + + const size_t trailingPos = suffixPos + suffix.size(); + if (trailingPos < name.size()) { + for (size_t i = trailingPos; i < name.size(); i++) { + unsigned char ch = static_cast(name[i]); + if (!std::isdigit(ch)) { + return nil; + } + } + } else if (trailingPos != name.size()) { + return nil; + } + + const std::string baseName = name.substr(0, suffixPos); + if (baseName.empty()) { + return nil; + } + + return objc_getProtocol(baseName.c_str()); +} +} // namespace void ObjCBridgeState::registerProtocolGlobals(napi_env env, napi_value global) { MDSectionOffset offset = metadata->protocolsOffset; @@ -23,7 +62,7 @@ auto name = metadata->resolveString(nameOffset); // protocolOffsets[name] = originalOffset; - auto objcProtocol = objc_getProtocol(name); + auto objcProtocol = resolveRuntimeProtocol(name); if (objcProtocol != nil) { mdProtocolsByPointer[objcProtocol] = originalOffset; } diff --git a/NativeScript/ffi/SignatureDispatch.h b/NativeScript/ffi/SignatureDispatch.h new file mode 100644 index 00000000..14367c8b --- /dev/null +++ b/NativeScript/ffi/SignatureDispatch.h @@ -0,0 +1,181 @@ +#ifndef NS_SIGNATURE_DISPATCH_H +#define NS_SIGNATURE_DISPATCH_H + +#include + +#include +#include +#include +#include +#include + +#include "Cif.h" +#include "js_native_api.h" + +namespace nativescript { + +enum class SignatureCallKind : uint8_t { + ObjCMethod = 1, + CFunction = 2, +}; + +using ObjCPreparedInvoker = void (*)(void* fnptr, void** avalues, void* rvalue); +using CFunctionPreparedInvoker = void (*)(void* fnptr, void** avalues, + void* rvalue); +using ObjCNapiInvoker = bool (*)(napi_env env, Cif* cif, void* fnptr, id self, + SEL selector, const napi_value* argv, + void* rvalue); +using CFunctionNapiInvoker = bool (*)(napi_env env, Cif* cif, void* fnptr, + const napi_value* argv, void* rvalue); + +struct ObjCDispatchEntry { + uint64_t dispatchId; + ObjCPreparedInvoker invoker; +}; + +struct CFunctionDispatchEntry { + uint64_t dispatchId; + CFunctionPreparedInvoker invoker; +}; + +struct ObjCNapiDispatchEntry { + uint64_t dispatchId; + ObjCNapiInvoker invoker; +}; + +struct CFunctionNapiDispatchEntry { + uint64_t dispatchId; + CFunctionNapiInvoker invoker; +}; + +inline constexpr uint64_t kSignatureHashOffsetBasis = 14695981039346656037ull; +inline constexpr uint64_t kSignatureHashPrime = 1099511628211ull; + +inline uint64_t hashBytesFnv1a(const void* data, size_t size, + uint64_t seed = kSignatureHashOffsetBasis) { + const auto* bytes = static_cast(data); + uint64_t hash = seed; + for (size_t i = 0; i < size; i++) { + hash ^= static_cast(bytes[i]); + hash *= kSignatureHashPrime; + } + return hash; +} + +inline uint64_t composeSignatureDispatchId(uint64_t signatureHash, + SignatureCallKind kind, + uint8_t flags) { + const uint8_t kindByte = static_cast(kind); + uint64_t hash = hashBytesFnv1a(&kindByte, sizeof(kindByte)); + hash = hashBytesFnv1a(&flags, sizeof(flags), hash); + return hashBytesFnv1a(&signatureHash, sizeof(signatureHash), hash); +} + +} // namespace nativescript + +#ifndef NS_HAS_GENERATED_SIGNATURE_DISPATCH +#define NS_HAS_GENERATED_SIGNATURE_DISPATCH 0 +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_NAPI_DISPATCH +#define NS_HAS_GENERATED_SIGNATURE_NAPI_DISPATCH 0 +#endif + +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#endif +#endif + +#if !NS_HAS_GENERATED_SIGNATURE_DISPATCH +namespace nativescript { +inline constexpr ObjCDispatchEntry kGeneratedObjCDispatchEntries[] = { + {0, nullptr}}; +inline constexpr CFunctionDispatchEntry kGeneratedCFunctionDispatchEntries[] = { + {0, nullptr}}; +} // namespace nativescript +#endif + +#if !NS_HAS_GENERATED_SIGNATURE_NAPI_DISPATCH +namespace nativescript { +inline constexpr ObjCNapiDispatchEntry kGeneratedObjCNapiDispatchEntries[] = { + {0, nullptr}}; +inline constexpr CFunctionNapiDispatchEntry + kGeneratedCFunctionNapiDispatchEntries[] = {{0, nullptr}}; +} // namespace nativescript +#endif + +namespace nativescript { + +template +inline Invoker lookupDispatchInvoker(const Entry (&entries)[N], + uint64_t dispatchId) { + if (dispatchId == 0 || N <= 1) { + return nullptr; + } + + size_t low = 1; + size_t high = N; + while (low < high) { + const size_t mid = low + ((high - low) >> 1); + const uint64_t midId = entries[mid].dispatchId; + if (midId < dispatchId) { + low = mid + 1; + } else { + high = mid; + } + } + + if (low < N && entries[low].dispatchId == dispatchId) { + return entries[low].invoker; + } + return nullptr; +} + +inline bool isGeneratedDispatchEnabled() { + static const bool enabled = []() { + const char* disableFlag = std::getenv("NS_DISABLE_GSD"); + if (disableFlag == nullptr || disableFlag[0] == '\0') { + return true; + } + return !(disableFlag[0] == '0' && disableFlag[1] == '\0'); + }(); + return enabled; +} + +inline ObjCPreparedInvoker lookupObjCPreparedInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedObjCDispatchEntries, dispatchId); +} + +inline CFunctionPreparedInvoker lookupCFunctionPreparedInvoker( + uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedCFunctionDispatchEntries, dispatchId); +} + +inline ObjCNapiInvoker lookupObjCNapiInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedObjCNapiDispatchEntries, dispatchId); +} + +inline CFunctionNapiInvoker lookupCFunctionNapiInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedCFunctionNapiDispatchEntries, dispatchId); +} + +} // namespace nativescript + +#endif // NS_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/Struct.h b/NativeScript/ffi/Struct.h index 74d2de66..b4b4a728 100644 --- a/NativeScript/ffi/Struct.h +++ b/NativeScript/ffi/Struct.h @@ -36,8 +36,11 @@ class StructObject { void* data; StructInfo* info; bool owned; + napi_env env = nullptr; + napi_ref backingRef = nullptr; - StructObject(StructInfo* info, void* data = nullptr); + StructObject(StructInfo* info, void* data = nullptr, napi_env env = nullptr, + napi_value backingValue = nullptr); StructObject(napi_env env, StructInfo* info, napi_value object, void* memory = nullptr); diff --git a/NativeScript/ffi/Struct.mm b/NativeScript/ffi/Struct.mm index 254d5a19..07202667 100644 --- a/NativeScript/ffi/Struct.mm +++ b/NativeScript/ffi/Struct.mm @@ -1,5 +1,8 @@ #include "Struct.h" +#include #include +#include +#include "Interop.h" #include "ObjCBridge.h" #include "TypeConv.h" #include "Util.h" @@ -37,16 +40,58 @@ TypeConv::Make(env, metadata, &offset); } - napi_property_descriptor prop = { - .utf8name = name, - .method = nullptr, - .getter = JS_structGetter, - .setter = nullptr, - .value = nullptr, - .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), - .data = (void*)((size_t)originalOffset), - }; - napi_define_properties(env, global, 1, &prop); + bool hasPrimaryName = false; + napi_has_named_property(env, global, name, &hasPrimaryName); + if (!hasPrimaryName) { + napi_property_descriptor prop = { + .utf8name = name, + .method = nullptr, + .getter = JS_structGetter, + .setter = nullptr, + .value = nullptr, + .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), + .data = (void*)((size_t)originalOffset), + }; + napi_define_properties(env, global, 1, &prop); + } + + if (!nameStr.empty() && nameStr[0] == '_') { + std::string alias = nameStr.substr(1); + if (!alias.empty()) { + bool hasAlias = false; + napi_has_named_property(env, global, alias.c_str(), &hasAlias); + if (!hasAlias) { + napi_property_descriptor aliasProp = { + .utf8name = alias.c_str(), + .method = nullptr, + .getter = JS_structGetter, + .setter = nullptr, + .value = nullptr, + .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), + .data = (void*)((size_t)originalOffset), + }; + napi_define_properties(env, global, 1, &aliasProp); + } + } + } + + if (nameStr.size() < 6 || nameStr.compare(nameStr.size() - 6, 6, "Struct") != 0) { + std::string alias = nameStr + "Struct"; + bool hasAlias = false; + napi_has_named_property(env, global, alias.c_str(), &hasAlias); + if (!hasAlias) { + napi_property_descriptor aliasProp = { + .utf8name = alias.c_str(), + .method = nullptr, + .getter = JS_structGetter, + .setter = nullptr, + .value = nullptr, + .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), + .data = (void*)((size_t)originalOffset), + }; + napi_define_properties(env, global, 1, &aliasProp); + } + } } } @@ -184,11 +229,19 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { NAPI_FUNCTION(StructConstructor) { NAPI_PREAMBLE - napi_value jsThis, arg; + napi_value jsThis; + napi_value argv[1]; StructInfo* info; size_t argc = 1; - napi_get_cb_info(env, cbinfo, &argc, &arg, &jsThis, (void**)&info); + napi_get_cb_info(env, cbinfo, &argc, argv, &jsThis, (void**)&info); + + napi_value arg; + if (argc > 0) { + arg = argv[0]; + } else { + napi_get_undefined(env, &arg); + } napi_valuetype argType; napi_typeof(env, arg, &argType); @@ -196,7 +249,23 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { StructObject* object; if (argType == napi_object) { - object = new StructObject(env, info, arg); + if (Pointer::isInstance(env, arg)) { + Pointer* pointer = Pointer::unwrap(env, arg); + if (pointer == nullptr) { + napi_throw_error(env, nullptr, "Invalid pointer-backed struct argument"); + return nullptr; + } + object = new StructObject(info, pointer->data, env, arg); + } else if (Reference::isInstance(env, arg)) { + Reference* reference = Reference::unwrap(env, arg); + if (reference == nullptr || reference->data == nullptr) { + napi_throw_error(env, nullptr, "Reference is not initialized"); + return nullptr; + } + object = new StructObject(info, reference->data, env, arg); + } else { + object = new StructObject(env, info, arg); + } } else { object = new StructObject(info); } @@ -216,7 +285,13 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { napi_get_cb_info(env, cbinfo, nullptr, nullptr, &jsThis, (void**)&info); auto object = StructObject::unwrap(env, jsThis); - return object->get(env, info); + auto value = object->get(env, info); + + if (StructObject::isInstance(env, value)) { + napi_set_named_property(env, value, "__ns_parent_struct", jsThis); + } + + return value; } NAPI_FUNCTION(StructPropertySetter) { @@ -248,8 +323,57 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { return result; } -inline StructObject::StructObject(StructInfo* info, void* data) { +NAPI_FUNCTION(StructEquals) { + napi_value jsThis, argv[2]; + size_t argc = 2; + void* data = nullptr; + napi_get_cb_info(env, cbinfo, &argc, argv, &jsThis, &data); + + StructInfo* info = static_cast(data); + if (info == nullptr || argc < 2) { + napi_value result; + napi_get_boolean(env, false, &result); + return result; + } + + auto serialize = [&](napi_value value, std::vector& out) -> bool { + out.assign(info->size, 0); + + StructObject* structObject = StructObject::unwrap(env, value); + if (structObject != nullptr) { + size_t copySize = + std::min(static_cast(info->size), static_cast(structObject->info->size)); + memcpy(out.data(), structObject->data, copySize); + return true; + } + + napi_valuetype type = napi_undefined; + napi_typeof(env, value, &type); + if (type != napi_object) { + return false; + } + + StructObject(env, info, value, out.data()); + bool pending = false; + napi_is_exception_pending(env, &pending); + return !pending; + }; + + std::vector left; + std::vector right; + bool okLeft = serialize(argv[0], left); + bool okRight = serialize(argv[1], right); + + napi_value result; + napi_get_boolean(env, okLeft && okRight && memcmp(left.data(), right.data(), info->size) == 0, + &result); + return result; +} + +inline StructObject::StructObject(StructInfo* info, void* data, napi_env env, + napi_value backingValue) { this->info = info; + this->env = env; if (data == nullptr) { this->data = malloc(info->size); memset(this->data, 0, this->info->size); @@ -257,11 +381,15 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { } else { this->data = data; this->owned = false; + if (env != nullptr && backingValue != nullptr) { + napi_create_reference(env, backingValue, 1, &this->backingRef); + } } } StructObject::StructObject(napi_env env, StructInfo* info, napi_value object, void* memory) { this->info = info; + this->env = env; if (memory == nullptr) { this->owned = true; @@ -271,6 +399,8 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { this->data = memory; } + memset(this->data, 0, this->info->size); + for (auto& field : info->fields) { bool hasProp = false; napi_has_named_property(env, object, field.name, &hasProp); @@ -284,6 +414,9 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { } StructObject::~StructObject() { + if (this->backingRef != nullptr && this->env != nullptr) { + napi_delete_reference(this->env, this->backingRef); + } if (this->owned) free(this->data); } @@ -349,7 +482,28 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { napi_define_class(env, info->name, NAPI_AUTO_LENGTH, JS_StructConstructor, (void*)info, info->fields.size() + 2, properties, &result); - napi_define_properties(env, result, 1, sizeofProp); + const napi_property_descriptor classProps[] = { + { + .utf8name = "equals", + .method = JS_StructEquals, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_default, + .data = info, + }, + { + .utf8name = nullptr, + .name = jsSymbolFor(env, "sizeof"), + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = size, + .attributes = napi_enumerable, + .data = nullptr, + }, + }; + napi_define_properties(env, result, 2, classProps); free(properties); @@ -357,12 +511,18 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { } bool StructObject::isInstance(napi_env env, napi_value object) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, object, &valueType); + if (valueType != napi_object && valueType != napi_function) { + return false; + } + napi_value sizeofSymbol = jsSymbolFor(env, "sizeof"); - napi_value prop; - if (napi_get_property(env, object, sizeofSymbol, &prop) == napi_ok) { - return true; + bool hasProp = false; + if (napi_has_property(env, object, sizeofSymbol, &hasProp) != napi_ok) { + return false; } - return false; + return hasProp; } napi_value StructObject::getJSClass(napi_env env, StructInfo* info) { @@ -381,6 +541,10 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { napi_value cls = getJSClass(env, info); napi_new_instance(env, cls, 0, nullptr, &result); auto object = StructObject::unwrap(env, result); + if (object == nullptr) { + return result; + } + if (owned) { if (object->owned) { memcpy(object->data, data, info->size); @@ -390,7 +554,9 @@ void StructObject_finalize(napi_env env, void* data, void* hint) { object->owned = true; } } else { - object->~StructObject(); + if (object->owned && object->data != nullptr) { + free(object->data); + } object->data = data; object->owned = false; } diff --git a/NativeScript/ffi/Tasks.cpp b/NativeScript/ffi/Tasks.cpp index 54a21783..e01e002d 100644 --- a/NativeScript/ffi/Tasks.cpp +++ b/NativeScript/ffi/Tasks.cpp @@ -1,9 +1,10 @@ #include "Tasks.h" +#include namespace nativescript { void Tasks::Register(std::function task) { - tasks_.push_back(task); + tasks_.emplace_back(std::move(task)); } void Tasks::Drain() { diff --git a/NativeScript/ffi/TypeConv.h b/NativeScript/ffi/TypeConv.h index 721c0868..4e6dfcee 100644 --- a/NativeScript/ffi/TypeConv.h +++ b/NativeScript/ffi/TypeConv.h @@ -16,6 +16,8 @@ typedef enum ConvertToJSFlags : uint32_t { kReturnOwned = 1 << 0, kBlockParam = 1 << 1, kStructZeroCopy = 1 << 2, + kCStringAsReference = 1 << 3, + kCFunctionObjectReturn = 1 << 4, } ConvertToJSFlags; class TypeConv { @@ -37,9 +39,23 @@ class TypeConv { virtual void free(napi_env env, void* value) {} + virtual ffi_type* ffiTypeForArgument() { return type; } + virtual void encode(std::string* encoding) {} }; +// Fast-path conversion for known metadata kinds used by generated dispatch +// wrappers. Returns true only when conversion is fully handled and written to +// `result`. Returns false when caller should fall back to TypeConv::toNative. +bool TryFastConvertNapiArgument(napi_env env, MDTypeKind kind, napi_value value, + void* result); + +// Fast direct conversion for uint16_t / unichar arguments used by generated +// dispatch wrappers. Supports both numeric values and single-character JS +// strings. +bool TryFastConvertNapiUInt16Argument(napi_env env, napi_value value, + uint16_t* result); + // Cleanup function to clear thread-local struct type caches void clearStructTypeCaches(); diff --git a/NativeScript/ffi/TypeConv.mm b/NativeScript/ffi/TypeConv.mm index 335e9470..a72edd6e 100644 --- a/NativeScript/ffi/TypeConv.mm +++ b/NativeScript/ffi/TypeConv.mm @@ -16,15 +16,304 @@ #import #import #include +#if defined(__has_include) +#if __has_include() +#include +#endif +#endif #include +#include +#include +#include +#include #include #include #include #include #include +namespace { + +static size_t getBufferElementSize(napi_typedarray_type type) { + switch (type) { + case napi_int8_array: + case napi_uint8_array: + case napi_uint8_clamped_array: + return 1; + case napi_int16_array: + case napi_uint16_array: + return 2; + case napi_int32_array: + case napi_uint32_array: + case napi_float32_array: + return 4; + case napi_float64_array: + case napi_bigint64_array: + case napi_biguint64_array: + return 8; + default: + return 1; + } +} + +static bool getJSBufferData(napi_env env, napi_value value, void** data, size_t* byteLength) { + if (data == nullptr || byteLength == nullptr) { + return false; + } + + bool isArrayBuffer = false; + if (napi_is_arraybuffer(env, value, &isArrayBuffer) == napi_ok && isArrayBuffer) { + return napi_get_arraybuffer_info(env, value, data, byteLength) == napi_ok; + } + + bool isTypedArray = false; + if (napi_is_typedarray(env, value, &isTypedArray) == napi_ok && isTypedArray) { + napi_typedarray_type type; + napi_value arrayBuffer; + size_t byteOffset = 0; + size_t elementLength = 0; + if (napi_get_typedarray_info(env, value, &type, &elementLength, data, &arrayBuffer, + &byteOffset) != napi_ok) { + return false; + } + + *byteLength = elementLength * getBufferElementSize(type); + return true; + } + + bool isDataView = false; + if (napi_is_dataview(env, value, &isDataView) == napi_ok && isDataView) { + napi_value arrayBuffer; + size_t byteOffset = 0; + return napi_get_dataview_info(env, value, byteLength, data, &arrayBuffer, &byteOffset) == + napi_ok; + } + + return false; +} + +static const void* kNSDataJSValueAssociationKey = &kNSDataJSValueAssociationKey; + +static id resolveCachedHandleObject(napi_env env, void* handle) { + if (env == nullptr || handle == nullptr) { + return nil; + } + + auto bridgeState = nativescript::ObjCBridgeState::InstanceData(env); + if (bridgeState == nullptr) { + return nil; + } + + napi_value cachedValue = bridgeState->getCachedHandleObject(env, handle); + if (cachedValue == nullptr) { + return nil; + } + + void* wrapped = nullptr; + if (napi_unwrap(env, cachedValue, &wrapped) == napi_ok && wrapped != nullptr) { + bridgeState->cacheRoundTripObject(env, static_cast(wrapped), cachedValue); + return static_cast(wrapped); + } + + bool hasNativePointer = false; + if (napi_has_named_property(env, cachedValue, "__ns_native_ptr", &hasNativePointer) == napi_ok && + hasNativePointer) { + napi_value nativePointerValue = nullptr; + void* nativePointer = nullptr; + if (napi_get_named_property(env, cachedValue, "__ns_native_ptr", &nativePointerValue) == + napi_ok && + napi_get_value_external(env, nativePointerValue, &nativePointer) == napi_ok && + nativePointer != nullptr) { + bridgeState->cacheRoundTripObject(env, static_cast(nativePointer), cachedValue); + return static_cast(nativePointer); + } + } + + return nil; +} + +} // namespace + +@interface NSDataJSValueAssociation : NSObject + +- (instancetype)initWithEnv:(napi_env)env + value:(napi_value)value + bridgeState:(nativescript::ObjCBridgeState*)bridgeState; + +@end + +@implementation NSDataJSValueAssociation { + napi_env env_; + nativescript::ObjCBridgeState* bridgeState_; + uint64_t bridgeStateToken_; + napi_ref valueRef_; +} + +- (instancetype)initWithEnv:(napi_env)env + value:(napi_value)value + bridgeState:(nativescript::ObjCBridgeState*)bridgeState { + self = [super init]; + if (self != nil) { + env_ = env; + bridgeState_ = bridgeState; + bridgeStateToken_ = bridgeState != nullptr ? bridgeState->lifetimeToken : 0; + valueRef_ = nativescript::make_ref(env, value); + } + + return self; +} + +- (void)dealloc { + if (valueRef_ != nullptr && env_ != nullptr && + nativescript::IsBridgeStateLive(bridgeState_, bridgeStateToken_)) { + napi_delete_reference(env_, valueRef_); + } + + [super dealloc]; +} + +@end + namespace nativescript { +namespace { +constexpr const char* kProtocolSuffix = "Protocol"; + +NSData* createNSDataWrapper(napi_env env, napi_value value, ObjCBridgeState* bridgeState) { + void* data = nullptr; + size_t byteLength = 0; + if (!getJSBufferData(env, value, &data, &byteLength)) { + return nil; + } + + NSData* wrappedData = [NSData dataWithBytesNoCopy:data length:byteLength freeWhenDone:NO]; + if (wrappedData == nil) { + return nil; + } + + NSDataJSValueAssociation* association = + [[NSDataJSValueAssociation alloc] initWithEnv:env value:value bridgeState:bridgeState]; + objc_setAssociatedObject(wrappedData, kNSDataJSValueAssociationKey, association, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + [association release]; + + return wrappedData; +} + +inline size_t alignUp(size_t value, size_t alignment) { + if (alignment == 0) { + return value; + } + return ((value + alignment - 1) / alignment) * alignment; +} + +inline uintptr_t normalizeRuntimePointer(uintptr_t ptr) { +#if INTPTR_MAX == INT64_MAX + return ptr & 0x0000FFFFFFFFFFFFULL; +#else + return ptr; +#endif +} + +inline bool isKindOfClassFast(id obj, Class expectedClass) { + if (obj == nil || expectedClass == Nil) { + return false; + } + + return [obj isKindOfClass:expectedClass]; +} + +bool stripProtocolSuffix(const char* name, std::string* out) { + if (name == nullptr || out == nullptr) { + return false; + } + + const size_t nameLen = std::strlen(name); + const size_t suffixLen = std::strlen(kProtocolSuffix); + if (nameLen <= suffixLen) { + return false; + } + + if (std::strcmp(name + (nameLen - suffixLen), kProtocolSuffix) != 0) { + return false; + } + + *out = std::string(name, nameLen - suffixLen); + return !out->empty(); +} + +bool protocolNamesMatch(const char* metadataName, const char* runtimeName) { + if (metadataName == nullptr || runtimeName == nullptr) { + return false; + } + + if (std::strcmp(metadataName, runtimeName) == 0) { + return true; + } + + std::string metadataBase(metadataName); + std::string runtimeBase(runtimeName); + stripProtocolSuffix(metadataName, &metadataBase); + stripProtocolSuffix(runtimeName, &runtimeBase); + + return metadataBase == runtimeBase; +} + +MDSectionOffset findProtocolMetadataOffset(MDMetadataReader* metadata, const char* protocolName) { + if (metadata == nullptr || protocolName == nullptr) { + return MD_SECTION_OFFSET_NULL; + } + + MDSectionOffset offset = metadata->protocolsOffset; + while (offset < metadata->classesOffset) { + MDSectionOffset originalOffset = offset; + + auto nameOffset = metadata->getOffset(offset); + offset += sizeof(MDSectionOffset); + bool next = (nameOffset & mdSectionOffsetNext) != 0; + nameOffset &= ~mdSectionOffsetNext; + + auto name = metadata->resolveString(nameOffset); + if (protocolNamesMatch(name, protocolName)) { + return originalOffset; + } + + while (next) { + auto protocolImpl = metadata->getOffset(offset); + offset += sizeof(MDSectionOffset); + next = (protocolImpl & mdSectionOffsetNext) != 0; + } + + next = true; + while (next) { + auto flags = metadata->getMemberFlag(offset); + next = (flags & mdMemberNext) != 0; + offset += sizeof(flags); + + if (flags == mdMemberFlagNull) { + break; + } + + if ((flags & mdMemberProperty) != 0) { + bool readonly = (flags & mdMemberReadonly) != 0; + offset += sizeof(MDSectionOffset); // name + offset += sizeof(MDSectionOffset); // getter selector + offset += sizeof(MDSectionOffset); // getter signature + if (!readonly) { + offset += sizeof(MDSectionOffset); // setter selector + offset += sizeof(MDSectionOffset); // setter signature + } + } else { + offset += sizeof(MDSectionOffset); // selector + offset += sizeof(MDSectionOffset); // signature + } + } + } + + return MD_SECTION_OFFSET_NULL; +} +} // namespace + // Forward declaration class StructTypeConv; @@ -47,10 +336,21 @@ std::string structname; const char* nameStart = *encoding + 1; // skip '{' const char* c = nameStart; - while (*c != '=') { + while (*c != '\0' && *c != '=') { structname += *c; c++; } + if (*c != '=') { + // Malformed struct encoding. Advance to the end of this token and + // fallback to pointer conversion to avoid reading past the buffer. + while (**encoding != '\0' && **encoding != '}') { + (*encoding)++; + } + if (**encoding == '}') { + (*encoding)++; + } + return &ffi_type_pointer; + } // Check if we're already processing this struct (cycle detection) if (processingEncodingStructs.find(structname) != processingEncodingStructs.end()) { @@ -75,16 +375,18 @@ } // Check if we already have a forward declaration for this struct - auto forwardIt = forwardDeclaredEncodingStructs.find(structname); - if (forwardIt != forwardDeclaredEncodingStructs.end()) { + auto existingForwardIt = forwardDeclaredEncodingStructs.find(structname); + if (existingForwardIt != forwardDeclaredEncodingStructs.end()) { // Skip the struct encoding (*encoding)++; // skip '{' - while (**encoding != '}') { + while (**encoding != '\0' && **encoding != '}') { (*encoding)++; } - (*encoding)++; // skip '}' + if (**encoding == '}') { + (*encoding)++; // skip '}' + } - return forwardIt->second; + return existingForwardIt->second; } // Mark this struct as being processed @@ -100,18 +402,25 @@ (*encoding)++; // skip '{' - while (**encoding != '=') { + while (**encoding != '\0' && **encoding != '=') { (*encoding)++; } // skip name + if (**encoding == '\0') { + processingEncodingStructs.erase(structname); + delete type; + return &ffi_type_pointer; + } (*encoding)++; // skip '=' - while (**encoding != '}') { + while (**encoding != '\0' && **encoding != '}') { ffi_type* elementType = TypeConv::Make(env, encoding)->type; elements.push_back(elementType); } - (*encoding)++; // skip '}' + if (**encoding == '}') { + (*encoding)++; // skip '}' + } type->elements = (ffi_type**)malloc(sizeof(ffi_type*) * (elements.size() + 1)); for (int i = 0; i < elements.size(); i++) { @@ -121,8 +430,9 @@ type->elements[elements.size()] = nullptr; // If this was a forward declaration, update it with the real layout - if (forwardIt != forwardDeclaredEncodingStructs.end()) { - ffi_type* forwardType = forwardIt->second; + auto resolvedForwardIt = forwardDeclaredEncodingStructs.find(structname); + if (resolvedForwardIt != forwardDeclaredEncodingStructs.end()) { + ffi_type* forwardType = resolvedForwardIt->second; forwardType->type = type->type; forwardType->size = type->size; forwardType->alignment = type->alignment; @@ -131,7 +441,7 @@ // Clean up the temporary type and use the forward declaration delete type; type = forwardType; - forwardDeclaredEncodingStructs.erase(forwardIt); + forwardDeclaredEncodingStructs.erase(resolvedForwardIt); } // Remove from processing set @@ -157,9 +467,9 @@ } // Check if we already have a forward declaration for this struct - auto forwardIt = forwardDeclaredStructs.find(structOffset); - if (forwardIt != forwardDeclaredStructs.end()) { - return forwardIt->second; + auto existingForwardIt = forwardDeclaredStructs.find(structOffset); + if (existingForwardIt != forwardDeclaredStructs.end()) { + return existingForwardIt->second; } // Mark this struct as being processed @@ -200,8 +510,9 @@ type->elements[elements.size()] = nullptr; // If this was a forward declaration, update it with the real layout - if (forwardIt != forwardDeclaredStructs.end()) { - ffi_type* forwardType = forwardIt->second; + auto resolvedForwardIt = forwardDeclaredStructs.find(structOffset); + if (resolvedForwardIt != forwardDeclaredStructs.end()) { + ffi_type* forwardType = resolvedForwardIt->second; forwardType->type = type->type; forwardType->size = type->size; forwardType->alignment = type->alignment; @@ -210,7 +521,7 @@ // Clean up the temporary type and use the forward declaration delete type; type = forwardType; - forwardDeclaredStructs.erase(forwardIt); + forwardDeclaredStructs.erase(resolvedForwardIt); } // Remove from processing set @@ -243,7 +554,10 @@ static inline size_t getTypedArrayUnitLength(napi_typedarray_type type) { class VoidTypeConv : public TypeConv { public: - VoidTypeConv() { type = &ffi_type_void; } + VoidTypeConv() { + type = &ffi_type_void; + kind = mdTypeVoid; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; @@ -258,11 +572,21 @@ napi_value toJS(napi_env env, void* value, uint32_t flags) override { class SCharTypeConv : public TypeConv { public: - SCharTypeConv() { type = &ffi_type_schar; } + SCharTypeConv() { + type = &ffi_type_schar; + kind = mdTypeChar; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { + int8_t raw = *(int8_t*)value; + if (raw == 0 || raw == 1) { + napi_value result; + napi_get_boolean(env, raw == 1, &result); + return result; + } + napi_value result; - napi_create_int32(env, *(int8_t*)value, &result); + napi_create_int32(env, raw, &result); return result; } @@ -281,11 +605,21 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class UCharTypeConv : public TypeConv { public: - UCharTypeConv() { type = &ffi_type_uchar; } + UCharTypeConv() { + type = &ffi_type_uchar; + kind = mdTypeUChar; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { + uint8_t raw = *(uint8_t*)value; + if (raw == 0 || raw == 1) { + napi_value result; + napi_get_boolean(env, raw == 1, &result); + return result; + } + napi_value result; - napi_create_uint32(env, *(uint8_t*)value, &result); + napi_create_uint32(env, raw, &result); return result; } @@ -304,11 +638,21 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class UInt8TypeConv : public TypeConv { public: - UInt8TypeConv() { type = &ffi_type_uint8; } + UInt8TypeConv() { + type = &ffi_type_uint8; + kind = mdTypeUInt8; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { + uint8_t raw = *(uint8_t*)value; + if (raw == 0 || raw == 1) { + napi_value result; + napi_get_boolean(env, raw == 1, &result); + return result; + } + napi_value result; - napi_create_uint32(env, *(uint8_t*)value, &result); + napi_create_uint32(env, raw, &result); return result; } @@ -327,7 +671,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class SInt16TypeConv : public TypeConv { public: - SInt16TypeConv() { type = &ffi_type_sshort; } + SInt16TypeConv() { + type = &ffi_type_sshort; + kind = mdTypeSShort; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; @@ -350,17 +697,46 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class UInt16TypeConv : public TypeConv { public: - UInt16TypeConv() { type = &ffi_type_ushort; } + UInt16TypeConv() { + type = &ffi_type_ushort; + kind = mdTypeUShort; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { + uint16_t raw = *(uint16_t*)value; + if (raw >= 32 && raw <= 126) { + char buffer[2] = {static_cast(raw), '\0'}; + napi_value result; + napi_create_string_utf8(env, buffer, NAPI_AUTO_LENGTH, &result); + return result; + } + napi_value result; - napi_create_uint32(env, *(uint16_t*)value, &result); + napi_create_uint32(env, raw, &result); return result; } void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, bool* shouldFreeAny) override { - uint32_t val; + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + + if (valueType == napi_string) { + size_t strLen = 0; + napi_get_value_string_utf16(env, value, nullptr, 0, &strLen); + if (strLen != 1) { + napi_throw_type_error(env, nullptr, "Expected a single-character string."); + *(uint16_t*)result = 0; + return; + } + + char16_t chars[2] = {0, 0}; + napi_get_value_string_utf16(env, value, chars, 2, &strLen); + *(uint16_t*)result = static_cast(chars[0]); + return; + } + + uint32_t val = 0; napi_coerce_to_number(env, value, &value); napi_get_value_uint32(env, value, &val); *(uint16_t*)result = val; @@ -373,7 +749,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class SInt32TypeConv : public TypeConv { public: - SInt32TypeConv() { type = &ffi_type_sint; } + SInt32TypeConv() { + type = &ffi_type_sint; + kind = mdTypeSInt; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; @@ -396,7 +775,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class UInt32TypeConv : public TypeConv { public: - UInt32TypeConv() { type = &ffi_type_uint; } + UInt32TypeConv() { + type = &ffi_type_uint; + kind = mdTypeUInt; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; @@ -419,12 +801,20 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class SInt64TypeConv : public TypeConv { public: - SInt64TypeConv() { type = &ffi_type_sint64; } + SInt64TypeConv() { + type = &ffi_type_sint64; + kind = mdTypeSInt64; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; int64_t val = *(int64_t*)value; - napi_create_int64(env, val, &result); + constexpr int64_t kMaxSafeInteger = 9007199254740991LL; + if (val > kMaxSafeInteger || val < -kMaxSafeInteger) { + napi_create_bigint_int64(env, val, &result); + } else { + napi_create_int64(env, val, &result); + } return result; } @@ -462,12 +852,20 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class UInt64TypeConv : public TypeConv { public: - UInt64TypeConv() { type = &ffi_type_uint64; } + UInt64TypeConv() { + type = &ffi_type_uint64; + kind = mdTypeUInt64; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; uint64_t val = *(uint64_t*)value; - napi_create_int64(env, (int64_t)val, &result); + constexpr uint64_t kMaxSafeInteger = 9007199254740991ULL; + if (val > kMaxSafeInteger) { + napi_create_bigint_uint64(env, val, &result); + } else { + napi_create_int64(env, static_cast(val), &result); + } return result; } @@ -512,7 +910,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, }}; public: - UInt128TypeConv() { type = &_type; } + UInt128TypeConv() { + type = &_type; + kind = mdTypeUInt128; + } // TODO @@ -548,7 +949,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class Float32TypeConv : public TypeConv { public: - Float32TypeConv() { type = &ffi_type_float; } + Float32TypeConv() { + type = &ffi_type_float; + kind = mdTypeFloat; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; @@ -571,7 +975,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class Float64TypeConv : public TypeConv { public: - Float64TypeConv() { type = &ffi_type_double; } + Float64TypeConv() { + type = &ffi_type_double; + kind = mdTypeDouble; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; @@ -597,20 +1004,49 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class BoolTypeConv : public TypeConv { public: - BoolTypeConv() { type = &ffi_type_uint8; } + BoolTypeConv() { + type = &ffi_type_uint8; + kind = mdTypeBool; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { + uint8_t raw = *(uint8_t*)value; + if (raw == 0 || raw == 1) { + napi_value result; + napi_get_boolean(env, raw == 1, &result); + return result; + } + napi_value result; - napi_get_boolean(env, *(bool*)value, &result); + napi_create_uint32(env, raw, &result); return result; } void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, bool* shouldFreeAny) override { - bool val; + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + + if (valueType == napi_number) { + uint32_t val = 0; + napi_coerce_to_number(env, value, &value); + napi_get_value_uint32(env, value, &val); + *(uint8_t*)result = static_cast(val); + return; + } + + if (valueType == napi_bigint) { + uint64_t val = 0; + bool lossless = false; + napi_get_value_bigint_uint64(env, value, &val, &lossless); + *(uint8_t*)result = static_cast(val); + return; + } + + bool val = false; napi_coerce_to_bool(env, value, &value); napi_get_value_bool(env, value, &val); - *(bool*)result = val; + *(uint8_t*)result = static_cast(val ? 1 : 0); } void encode(std::string* encoding) override { *encoding += "B"; } @@ -622,14 +1058,114 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, public: std::shared_ptr pointeeType = nullptr; - PointerTypeConv() { type = &ffi_type_pointer; } + PointerTypeConv() { + type = &ffi_type_pointer; + kind = mdTypePointer; + } PointerTypeConv(std::shared_ptr pointeeType) : pointeeType(pointeeType) { type = &ffi_type_pointer; + kind = mdTypePointer; } napi_value toJS(napi_env env, void* value, uint32_t flags) override { - return Pointer::create(env, *((void**)value)); + void* raw = *((void**)value); + if (raw == nullptr) { + napi_value nullValue; + napi_get_null(env, &nullValue); + return nullValue; + } + + auto normalizePtr = [](void* ptr) -> uintptr_t { +#if INTPTR_MAX == INT64_MAX + // Objective-C pointers may carry auth/tag bits on some runtimes. + // Compare using canonical lower bits for stable lookups. + return reinterpret_cast(ptr) & 0x0000FFFFFFFFFFFFULL; +#else + return reinterpret_cast(ptr); +#endif + }; + + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState != nullptr) { + auto classIt = bridgeState->mdClassesByPointer.find((Class)raw); + if (classIt != bridgeState->mdClassesByPointer.end()) { + auto cls = bridgeState->getClass(env, classIt->second); + if (cls != nullptr) { + return get_ref_value(env, cls->constructor); + } + } else { + const uintptr_t rawNormalized = normalizePtr(raw); + for (const auto& entry : bridgeState->mdClassesByPointer) { + if (normalizePtr((void*)entry.first) != rawNormalized) { + continue; + } + + auto cls = bridgeState->getClass(env, entry.second); + if (cls != nullptr) { + return get_ref_value(env, cls->constructor); + } + } + } + + auto protocolIt = bridgeState->mdProtocolsByPointer.find((Protocol*)raw); + if (protocolIt != bridgeState->mdProtocolsByPointer.end()) { + auto proto = bridgeState->getProtocol(env, protocolIt->second); + if (proto != nullptr) { + return get_ref_value(env, proto->constructor); + } + } else { + const uintptr_t rawNormalized = normalizePtr(raw); + for (const auto& entry : bridgeState->mdProtocolsByPointer) { + if (normalizePtr((void*)entry.first) != rawNormalized) { + continue; + } + + auto proto = bridgeState->getProtocol(env, entry.second); + if (proto != nullptr) { + return get_ref_value(env, proto->constructor); + } + } + + // Some protocol pointers come from compile-time @protocol() references + // and don't always match objc_getProtocol() pointer identity. + // Resolve them by scanning runtime protocol list and matching by address. + unsigned int protocolCount = 0; + Protocol** protocols = objc_copyProtocolList(&protocolCount); + if (protocols != nullptr) { + for (unsigned int i = 0; i < protocolCount; i++) { + Protocol* runtimeProto = protocols[i]; + if (normalizePtr((void*)runtimeProto) != rawNormalized) { + continue; + } + + const char* runtimeName = protocol_getName(runtimeProto); + MDSectionOffset metadataOffset = + findProtocolMetadataOffset(bridgeState->metadata, runtimeName); + if (metadataOffset != MD_SECTION_OFFSET_NULL) { + bridgeState->mdProtocolsByPointer[runtimeProto] = metadataOffset; + auto proto = bridgeState->getProtocol(env, metadataOffset); + if (proto != nullptr) { + ::free(protocols); + return get_ref_value(env, proto->constructor); + } + } + + break; + } + ::free(protocols); + } + } + } + + if (pointeeType != nullptr && pointeeType->kind != mdTypeVoid) { + napi_value referenceValue = Reference::create(env, pointeeType, raw, false); + if (referenceValue != nullptr) { + return referenceValue; + } + } + + return Pointer::create(env, raw); } void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, @@ -638,6 +1174,66 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, void** res = (void**)result; + auto unwrapKnownNativeHandle = [&](napi_value input, void** out) -> bool { + auto describeValue = [&](napi_value candidate) -> std::string { + if (candidate == nullptr) { + return "(null)"; + } + + napi_value nameValue = nullptr; + bool hasName = false; + if (napi_has_named_property(env, candidate, "name", &hasName) != napi_ok || !hasName) { + return "(unnamed)"; + } + + if (napi_get_named_property(env, candidate, "name", &nameValue) != napi_ok) { + return "(unnamed)"; + } + + napi_valuetype nameType = napi_undefined; + if (napi_typeof(env, nameValue, &nameType) != napi_ok || nameType != napi_string) { + return "(unnamed)"; + } + + char buffer[512]; + size_t length = 0; + if (napi_get_value_string_utf8(env, nameValue, buffer, sizeof(buffer), &length) != + napi_ok) { + return "(unnamed)"; + } + + return std::string(buffer, length); + }; + + void* wrapped = nullptr; + napi_status unwrapStatus = napi_unwrap(env, input, &wrapped); + if (unwrapStatus != napi_ok) { + return false; + } + + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState != nullptr) { + for (const auto& entry : bridgeState->classes) { + auto bridgedClass = entry.second; + if (bridgedClass == wrapped) { + *out = (void*)bridgedClass->nativeClass; + return true; + } + } + + for (const auto& entry : bridgeState->protocols) { + auto bridgedProtocol = entry.second; + if (bridgedProtocol == wrapped) { + *out = (void*)objc_getProtocol(bridgedProtocol->name.c_str()); + return true; + } + } + } + + *out = wrapped; + return true; + }; + napi_valuetype type; napi_typeof(env, value, &type); @@ -676,21 +1272,22 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, str[len] = '\0'; - // Check if we need to create a CFStringRef instead of a C string - // if (pointeeType && pointeeType->kind == mdTypeNSStringObject) { - // Create CFStringRef for CF/NS string pointers - CFStringRef cfStr = - CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8); - ::free(str); // Free the temporary C string - *res = (void*)cfStr; - *shouldFree = false; // CFStringRef is reference counted, don't use free() - *shouldFreeAny = false; - // } else { - // // Default behavior: return C string - // *res = (void*)str; - // *shouldFree = true; - // *shouldFreeAny = true; - // } + bool shouldCreateCFString = + pointeeType != nullptr && (pointeeType->kind == mdTypeNSStringObject || + pointeeType->kind == mdTypeNSMutableStringObject); + + if (shouldCreateCFString) { + CFStringRef cfStr = + CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8); + ::free(str); + *res = (void*)cfStr; + *shouldFree = true; + *shouldFreeAny = true; + } else { + *res = (void*)str; + *shouldFree = true; + *shouldFreeAny = true; + } return; } @@ -712,9 +1309,241 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, if (Reference::isInstance(env, value)) { Reference* ref = Reference::unwrap(env, value); + if (ref == nullptr) { + napi_throw_error(env, nullptr, "Invalid Reference"); + *res = nullptr; + return; + } if (ref->data == nullptr) { - ref->type = pointeeType; - ref->data = malloc(pointeeType->type->size); + std::shared_ptr resolvedType = pointeeType; + if (resolvedType == nullptr) { + resolvedType = ref->type; + } + + if (resolvedType == nullptr && ref->initValue != nullptr) { + napi_value initValue = get_ref_value(env, ref->initValue); + if (initValue != nullptr) { + napi_valuetype initType = napi_undefined; + if (napi_typeof(env, initValue, &initType) == napi_ok) { + auto makeStructType = [&](StructInfo* info) -> std::shared_ptr { + if (info == nullptr || info->name == nullptr) { + return nullptr; + } + + std::string encoding = "{"; + encoding += info->name; + encoding += "="; + for (const auto& field : info->fields) { + if (field.type == nullptr) { + return nullptr; + } + field.type->encode(&encoding); + } + encoding += "}"; + + const char* encodingPtr = encoding.c_str(); + return TypeConv::Make(env, &encodingPtr); + }; + + if (initType == napi_object) { + if (StructObject::isInstance(env, initValue)) { + StructObject* structObj = StructObject::unwrap(env, initValue); + if (structObj != nullptr) { + resolvedType = makeStructType(structObj->info); + } + } + + if (resolvedType == nullptr) { + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState != nullptr) { + bool isArray = false; + bool isTypedArray = false; + bool isArrayBuffer = false; + bool isDataView = false; + napi_is_array(env, initValue, &isArray); + napi_is_typedarray(env, initValue, &isTypedArray); + napi_is_arraybuffer(env, initValue, &isArrayBuffer); + napi_is_dataview(env, initValue, &isDataView); + if (!isArray && !isTypedArray && !isArrayBuffer && !isDataView) { + napi_value propertyNames = nullptr; + if (napi_get_property_names(env, initValue, &propertyNames) == napi_ok && + propertyNames != nullptr) { + uint32_t propertyCount = 0; + napi_get_array_length(env, propertyNames, &propertyCount); + std::unordered_set keys; + std::unordered_map keyIsInteger; + keys.reserve(propertyCount); + for (uint32_t i = 0; i < propertyCount; i++) { + napi_value keyValue = nullptr; + if (napi_get_element(env, propertyNames, i, &keyValue) != napi_ok || + keyValue == nullptr) { + continue; + } + napi_valuetype keyType = napi_undefined; + if (napi_typeof(env, keyValue, &keyType) != napi_ok || + keyType != napi_string) { + continue; + } + size_t keyLength = 0; + if (napi_get_value_string_utf8(env, keyValue, nullptr, 0, + &keyLength) != napi_ok) { + continue; + } + std::vector keyBuffer(keyLength + 1, '\0'); + if (napi_get_value_string_utf8(env, keyValue, keyBuffer.data(), + keyBuffer.size(), + &keyLength) != napi_ok) { + continue; + } + std::string key(keyBuffer.data(), keyLength); + keys.insert(key); + + napi_value propertyValue = nullptr; + if (napi_get_property(env, initValue, keyValue, &propertyValue) == + napi_ok && + propertyValue != nullptr) { + napi_valuetype propertyType = napi_undefined; + if (napi_typeof(env, propertyValue, &propertyType) == napi_ok) { + bool isInteger = false; + if (propertyType == napi_bigint) { + isInteger = true; + } else if (propertyType == napi_number) { + double numericValue = 0; + if (napi_get_value_double(env, propertyValue, &numericValue) == + napi_ok) { + int64_t truncated = static_cast(numericValue); + isInteger = static_cast(truncated) == numericValue; + } + } + keyIsInteger[key] = isInteger; + } + } + } + + if (!keys.empty()) { + auto isIntegerKind = [](MDTypeKind kind) -> bool { + switch (kind) { + case mdTypeChar: + case mdTypeSInt: + case mdTypeSShort: + case mdTypeSLong: + case mdTypeSInt64: + case mdTypeUChar: + case mdTypeUInt: + case mdTypeUShort: + case mdTypeULong: + case mdTypeUInt64: + case mdTypeUInt8: + case mdTypeBool: + return true; + default: + return false; + } + }; + + auto isFloatingKind = [](MDTypeKind kind) -> bool { + return kind == mdTypeFloat || kind == mdTypeDouble || + kind == mdTypeLongDouble || kind == mdTypeF16; + }; + + StructInfo* bestMatch = nullptr; + int bestScore = std::numeric_limits::min(); + uint16_t bestSize = std::numeric_limits::max(); + + for (const auto& entry : bridgeState->structOffsets) { + StructInfo* info = bridgeState->getStructInfo(env, entry.second); + if (info == nullptr || info->fields.size() != keys.size()) { + continue; + } + + bool match = true; + for (const auto& field : info->fields) { + if (field.name == nullptr || + keys.find(field.name) == keys.end()) { + match = false; + break; + } + } + if (!match) { + continue; + } + + int score = 0; + bool hasOnlyNumericFields = true; + for (const auto& field : info->fields) { + if (field.type == nullptr) { + hasOnlyNumericFields = false; + break; + } + + MDTypeKind fieldKind = field.type->kind; + if (isIntegerKind(fieldKind)) { + auto integerEntry = + keyIsInteger.find(field.name != nullptr ? field.name : ""); + score += + (integerEntry != keyIsInteger.end() && integerEntry->second) + ? 3 + : 1; + } else if (isFloatingKind(fieldKind)) { + score += 2; + } else { + hasOnlyNumericFields = false; + break; + } + } + + if (!hasOnlyNumericFields) { + continue; + } + + if (score > bestScore || + (score == bestScore && info->size < bestSize)) { + bestScore = score; + bestSize = info->size; + bestMatch = info; + } + } + + if (bestMatch != nullptr) { + resolvedType = makeStructType(bestMatch); + } + } + } + } + } + } + } + + if (resolvedType == nullptr) { + const char* inferredEncoding = "@"; + if (initType == napi_number || initType == napi_bigint) { + inferredEncoding = "q"; + } else if (initType == napi_boolean) { + inferredEncoding = "B"; + } + resolvedType = TypeConv::Make(env, &inferredEncoding); + } + } + } + } + + if (resolvedType == nullptr) { + const char* defaultEncoding = "@"; + resolvedType = TypeConv::Make(env, &defaultEncoding); + } + + ref->type = resolvedType; + size_t pointeeSize = sizeof(void*); + if (resolvedType != nullptr && resolvedType->type != nullptr && + resolvedType->type->size > 0) { + pointeeSize = resolvedType->type->size; + } + ref->data = calloc(1, pointeeSize); + if (ref->data == nullptr) { + napi_throw_error(env, nullptr, "Out of memory while allocating out parameter"); + *res = nullptr; + return; + } ref->ownsData = true; if (ref->initValue) { napi_value initValue = get_ref_value(env, ref->initValue); @@ -737,10 +1566,7 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, return; } - void* wrapped; - status = napi_unwrap(env, value, &wrapped); - if (status == napi_ok) { - *res = wrapped; + if (unwrapKnownNativeHandle(value, res)) { return; } @@ -760,27 +1586,49 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, *res = data; return; } + + bool isArrayBuffer = false; + napi_is_arraybuffer(env, value, &isArrayBuffer); + if (isArrayBuffer) { + void* data = nullptr; + size_t byteLength = 0; + napi_get_arraybuffer_info(env, value, &data, &byteLength); + *res = data; + return; + } + break; + } + + case napi_function: { + if (unwrapKnownNativeHandle(value, res)) { + return; + } + break; } default: - NSLog(@"value %d", type); napi_throw_error(env, nullptr, "Invalid pointer type"); *res = nullptr; return; } + + napi_throw_error(env, nullptr, "Invalid pointer type"); + *res = nullptr; } void free(napi_env env, void* value) override { - // if (pointeeType && pointeeType->kind == mdTypeNSStringObject) { - // CFStringRef needs CFRelease, not free() - CFStringRef cfStr = (CFStringRef)value; - if (cfStr != nullptr) { - CFRelease(cfStr); + if (value == nullptr) { + return; + } + + bool isCFString = pointeeType != nullptr && (pointeeType->kind == mdTypeNSStringObject || + pointeeType->kind == mdTypeNSMutableStringObject); + + if (isCFString) { + CFRelease((CFStringRef)value); + } else { + ::free(value); } - // } else { - // // Default behavior for C strings - // ::free(value); - // } } void encode(std::string* encoding) override { *encoding += "^v"; } @@ -794,10 +1642,17 @@ void free(napi_env env, void* value) override { BlockTypeConv(MDSectionOffset signatureOffset) : signatureOffset(signatureOffset) { type = &ffi_type_pointer; + kind = mdTypeBlock; } napi_value toJS(napi_env env, void* value, uint32_t flags) override { - return FunctionPointer::wrap(env, *((void**)value), signatureOffset, true); + void* fn = *((void**)value); + if (fn == nullptr) { + napi_value nullValue; + napi_get_null(env, &nullValue); + return nullValue; + } + return FunctionPointer::wrap(env, fn, signatureOffset, true); } void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, @@ -846,6 +1701,17 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, } case napi_function: { + if (FunctionReference::isInstance(env, value)) { + FunctionReference* ref = FunctionReference::unwrap(env, value); + if (ref == nullptr) { + napi_throw_error(env, nullptr, "Invalid FunctionReference"); + *res = nullptr; + return; + } + *res = ref->getFunctionPointer(signatureOffset, true); + return; + } + void* wrapped; status = napi_unwrap(env, value, &wrapped); if (status == napi_ok) { @@ -892,10 +1758,17 @@ void function_pointer_finalize(napi_env env, void* finalize_data, void* finalize FunctionPointerTypeConv(MDSectionOffset signatureOffset) : signatureOffset(signatureOffset) { type = &ffi_type_pointer; + kind = mdTypeFunctionPointer; } napi_value toJS(napi_env env, void* value, uint32_t flags) override { - return FunctionPointer::wrap(env, *((void**)value), signatureOffset, false); + void* fn = *((void**)value); + if (fn == nullptr) { + napi_value nullValue; + napi_get_null(env, &nullValue); + return nullValue; + } + return FunctionPointer::wrap(env, fn, signatureOffset, false); } void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, @@ -941,14 +1814,33 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, } else if (Reference::isInstance(env, value)) { Reference* ref = Reference::unwrap(env, value); *res = ref->data; - } else { + } else if (FunctionReference::isInstance(env, value)) { FunctionReference* ref = FunctionReference::unwrap(env, value); - *res = ref->getFunctionPointer(signatureOffset); + if (ref == nullptr) { + napi_throw_error(env, nullptr, "Invalid FunctionReference"); + *res = nullptr; + return; + } + *res = ref->getFunctionPointer(signatureOffset, false); + } else { + napi_throw_error(env, nullptr, "Invalid function pointer object"); + *res = nullptr; } return; } case napi_function: { + if (FunctionReference::isInstance(env, value)) { + FunctionReference* ref = FunctionReference::unwrap(env, value); + if (ref == nullptr) { + napi_throw_error(env, nullptr, "Invalid FunctionReference"); + *res = nullptr; + return; + } + *res = ref->getFunctionPointer(signatureOffset, false); + return; + } + void* wrapped; status = napi_unwrap(env, value, &wrapped); if (status == napi_ok) { @@ -957,7 +1849,7 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, } auto bridgeState = ObjCBridgeState::InstanceData(env); - auto closure = new Closure(bridgeState->metadata, signatureOffset, true); + auto closure = new Closure(bridgeState->metadata, signatureOffset, false); closure->env = env; closure->func = make_ref(env, value); napi_remove_wrap(env, value, nullptr); @@ -979,10 +1871,23 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class StringTypeConv : public TypeConv { public: - StringTypeConv() { type = &ffi_type_pointer; } + StringTypeConv() { + type = &ffi_type_pointer; + kind = mdTypeString; + } napi_value toJS(napi_env env, void* cont, uint32_t flags) override { void* value = *((void**)cont); + if (value == nullptr) { + napi_value null; + napi_get_null(env, &null); + return null; + } + + if ((flags & kCStringAsReference) != 0) { + return Reference::create(env, scharTypeConv, value, false); + } + napi_value result; napi_create_string_utf8(env, (char*)value, NAPI_AUTO_LENGTH, &result); return result; @@ -1019,6 +1924,45 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, return; } + bool isTypedArray = false; + napi_is_typedarray(env, value, &isTypedArray); + if (isTypedArray) { + void* data = nullptr; + size_t length = 0; + napi_typedarray_type typedArrayType; + napi_get_typedarray_info(env, value, &typedArrayType, &length, &data, nullptr, nullptr); + *(char**)result = static_cast(data); + *shouldFree = false; + *shouldFreeAny = false; + return; + } + + bool isArrayBuffer = false; + napi_is_arraybuffer(env, value, &isArrayBuffer); + if (isArrayBuffer) { + void* data = nullptr; + size_t byteLength = 0; + napi_get_arraybuffer_info(env, value, &data, &byteLength); + *(char**)result = static_cast(data); + *shouldFree = false; + *shouldFreeAny = false; + return; + } + + bool isDataView = false; + napi_is_dataview(env, value, &isDataView); + if (isDataView) { + void* data = nullptr; + size_t byteLength = 0; + napi_value arrayBuffer = nullptr; + size_t byteOffset = 0; + napi_get_dataview_info(env, value, &byteLength, &data, &arrayBuffer, &byteOffset); + *(char**)result = static_cast(data); + *shouldFree = false; + *shouldFreeAny = false; + return; + } + *(char**)result = nullptr; *shouldFree = false; *shouldFreeAny = false; @@ -1061,33 +2005,120 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, MDSectionOffset classOffset = 0; std::vector protocolOffsets; - ObjCObjectTypeConv() { type = &ffi_type_pointer; } + ObjCObjectTypeConv() { + type = &ffi_type_pointer; + kind = mdTypeAnyObject; + } ObjCObjectTypeConv(MDSectionOffset classOffset, std::vector protocolOffsets) : classOffset(classOffset), protocolOffsets(protocolOffsets) { type = &ffi_type_pointer; + if (classOffset != 0) { + kind = mdTypeClassObject; + } else { + kind = protocolOffsets.empty() ? mdTypeAnyObject : mdTypeProtocolObject; + } } ObjCObjectTypeConv(std::vector protocolOffsets) : protocolOffsets(protocolOffsets) { type = &ffi_type_pointer; + kind = protocolOffsets.empty() ? mdTypeAnyObject : mdTypeProtocolObject; } napi_value toJS(napi_env env, void* value, uint32_t flags) override { - id obj = *((id*)value); + void* rawPtr = *((void**)value); + id obj = (__bridge id)rawPtr; + if (obj == nil) { napi_value null; napi_get_null(env, &null); return null; } - if ([obj isKindOfClass:[NSMutableString class]]) { + auto bridgeState = ObjCBridgeState::InstanceData(env); + + if (bridgeState != nullptr) { + auto normalizePtr = [](void* ptr) -> uintptr_t { + return normalizeRuntimePointer(reinterpret_cast(ptr)); + }; + + auto protocolIt = bridgeState->mdProtocolsByPointer.find((Protocol*)obj); + if (protocolIt != bridgeState->mdProtocolsByPointer.end()) { + auto proto = bridgeState->getProtocol(env, protocolIt->second); + if (proto != nullptr) { + return get_ref_value(env, proto->constructor); + } + } else { + const uintptr_t objNormalized = normalizePtr((void*)obj); + for (const auto& entry : bridgeState->mdProtocolsByPointer) { + if (normalizePtr((void*)entry.first) != objNormalized) { + continue; + } + + auto proto = bridgeState->getProtocol(env, entry.second); + if (proto != nullptr) { + return get_ref_value(env, proto->constructor); + } + } + } + } + + // Always unbox NSNull and CFBoolean/NSNumber values (except NSDecimalNumber), + // so primitive round-trips match historical runtime behavior. + if (isKindOfClassFast(obj, [NSNull class])) { + napi_value null; + napi_get_null(env, &null); + return null; + } + + if (isKindOfClassFast(obj, [NSNumber class]) && + !isKindOfClassFast(obj, [NSDecimalNumber class])) { + if (CFGetTypeID((CFTypeRef)obj) == CFBooleanGetTypeID()) { + napi_value result; + napi_get_boolean(env, [obj boolValue], &result); + return result; + } + napi_value result; - napi_create_string_utf8(env, [obj UTF8String], [obj length], &result); + napi_create_double(env, [obj doubleValue], &result); return result; } - auto bridgeState = ObjCBridgeState::InstanceData(env); + // Untyped id values that are actually Objective-C blocks should be + // callable from JS. Preserve callback identity when we already have one. + if (isObjCBlockObject(obj)) { + napi_value cached = getCachedBlockCallback(env, (void*)obj); + if (cached != nullptr) { + return cached; + } + + const char* signature = getObjCBlockSignature((void*)obj); + if (signature != nullptr) { + return FunctionPointer::wrapWithEncoding(env, (void*)obj, signature, true); + } + } + + // Auto-unbox plain id string values. + const bool isUntypedObject = classOffset == 0 && protocolOffsets.empty(); + if (isUntypedObject && isKindOfClassFast(obj, [NSString class])) { + NSUInteger length = [obj length]; + std::vector chars(length > 0 ? length : 1); + if (length > 0) { + [((NSString*)obj) getCharacters:(unichar*)chars.data() range:NSMakeRange(0, length)]; + } + napi_value result; + napi_create_string_utf16(env, length > 0 ? chars.data() : nullptr, length, &result); + return result; + } + + if (bridgeState == nullptr) { + return Pointer::create(env, (void*)obj); + } + + if (napi_value existing = bridgeState->findCachedObjectWrapper(env, obj); existing != nullptr) { + return existing; + } ObjectOwnership ownership; if ((flags & kReturnOwned) != 0) { @@ -1123,24 +2154,23 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, case napi_string: { size_t len = 0; - NAPI_GUARD(napi_get_value_string_utf8(env, value, nullptr, len, &len)) { + NAPI_GUARD(napi_get_value_string_utf8(env, value, nullptr, 0, &len)) { NAPI_THROW_LAST_ERROR return; } - char* str = (char*)malloc(len + 1); - - NAPI_GUARD(napi_get_value_string_utf8(env, value, str, len + 1, &len)) { + std::vector chars(len + 1); + NAPI_GUARD(napi_get_value_string_utf8(env, value, chars.data(), len + 1, &len)) { NAPI_THROW_LAST_ERROR - ::free(str); return; } - str[len] = '\0'; - - *res = [[[NSString alloc] initWithUTF8String:str] autorelease]; - - ::free(str); + *res = [[[NSString alloc] initWithBytes:chars.data() + length:len + encoding:NSUTF8StringEncoding] autorelease]; + if (*res == nil) { + *res = [NSString string]; + } break; } @@ -1185,21 +2215,73 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, case napi_object: case napi_function: { + auto bridgeState = ObjCBridgeState::InstanceData(env); + auto cacheRoundTrip = [&](id nativeObj) { + if (nativeObj == nil || bridgeState == nullptr || + !bridgeState->hasRoundTripCacheFrame()) { + return; + } + + bridgeState->cacheRoundTripObject(env, nativeObj, value); + }; + if (Pointer::isInstance(env, value)) { Pointer* ptr = Pointer::unwrap(env, value); - *res = (id)ptr->data; + void* pointerData = ptr != nullptr ? ptr->data : nullptr; + if (id cachedObject = resolveCachedHandleObject(env, pointerData); cachedObject != nil) { + *res = cachedObject; + return; + } + *res = (id)pointerData; return; } if (Reference::isInstance(env, value)) { Reference* ref = Reference::unwrap(env, value); - *res = (id)ref->data; + void* referenceData = ref != nullptr ? ref->data : nullptr; + if (id cachedObject = resolveCachedHandleObject(env, referenceData); + cachedObject != nil) { + *res = cachedObject; + return; + } + *res = (id)referenceData; return; } - status = napi_unwrap(env, value, (void**)res); + void* wrapped = nullptr; + status = napi_unwrap(env, value, &wrapped); if (status != napi_ok) { + bool isArrayBuffer = false; + napi_is_arraybuffer(env, value, &isArrayBuffer); + if (isArrayBuffer) { + *res = createNSDataWrapper(env, value, bridgeState); + if (*res != nil) { + cacheRoundTrip(*res); + return; + } + } + + bool isTypedArray = false; + napi_is_typedarray(env, value, &isTypedArray); + if (isTypedArray) { + *res = createNSDataWrapper(env, value, bridgeState); + if (*res != nil) { + cacheRoundTrip(*res); + return; + } + } + + bool isDataView = false; + napi_is_dataview(env, value, &isDataView); + if (isDataView) { + *res = createNSDataWrapper(env, value, bridgeState); + if (*res != nil) { + cacheRoundTrip(*res); + return; + } + } + bool isArray = false; napi_is_array(env, value, &isArray); if (isArray) { @@ -1212,20 +2294,43 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, napi_get_element(env, value, i, &elem); id obj = nil; toNative(env, elem, (void*)&obj, shouldFree, shouldFreeAny); - [(*res) addObject:obj]; + [(*res) addObject:obj != nil ? obj : [NSNull null]]; } + cacheRoundTrip(*res); return; } else { - napi_value global, jsObject, valueConstructor, DateConstructor; + napi_value global, jsObject, valueConstructor, DateConstructor, MapConstructor; + napi_value StringConstructor, NumberConstructor, BooleanConstructor; napi_get_global(env, &global); napi_get_named_property(env, global, "Object", &jsObject); napi_get_named_property(env, global, "Date", &DateConstructor); + napi_get_named_property(env, global, "Map", &MapConstructor); + napi_get_named_property(env, global, "String", &StringConstructor); + napi_get_named_property(env, global, "Number", &NumberConstructor); + napi_get_named_property(env, global, "Boolean", &BooleanConstructor); napi_get_named_property(env, value, "constructor", &valueConstructor); bool isEqual; napi_strict_equals(env, jsObject, valueConstructor, &isEqual); bool isDate; napi_strict_equals(env, DateConstructor, valueConstructor, &isDate); + bool isMap; + napi_strict_equals(env, MapConstructor, valueConstructor, &isMap); + bool isStringObject = false; + bool isNumberObject = false; + bool isBooleanObject = false; + napi_strict_equals(env, StringConstructor, valueConstructor, &isStringObject); + napi_strict_equals(env, NumberConstructor, valueConstructor, &isNumberObject); + napi_strict_equals(env, BooleanConstructor, valueConstructor, &isBooleanObject); + + if (isStringObject || isNumberObject || isBooleanObject) { + napi_value valueOfMethod; + napi_get_named_property(env, value, "valueOf", &valueOfMethod); + napi_value primitiveValue; + napi_call_function(env, value, valueOfMethod, 0, nullptr, &primitiveValue); + toNative(env, primitiveValue, result, shouldFree, shouldFreeAny); + return; + } if (isDate) { // Get the timestamp from the JavaScript Date object @@ -1240,15 +2345,88 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, // Convert milliseconds to seconds for NSDate NSTimeInterval timeInSeconds = timeInMilliseconds / 1000.0; *res = [NSDate dateWithTimeIntervalSince1970:timeInSeconds]; + cacheRoundTrip(*res); + return; + } + + if (isMap) { + *res = [NSMutableDictionary dictionary]; + + napi_value entriesMethod; + napi_get_named_property(env, value, "entries", &entriesMethod); + napi_value iterator; + napi_call_function(env, value, entriesMethod, 0, nullptr, &iterator); + + napi_value nextMethod; + napi_get_named_property(env, iterator, "next", &nextMethod); + + while (true) { + napi_value step; + napi_call_function(env, iterator, nextMethod, 0, nullptr, &step); + + napi_value doneValue; + napi_get_named_property(env, step, "done", &doneValue); + bool done = false; + napi_get_value_bool(env, doneValue, &done); + if (done) { + break; + } + + napi_value tuple; + napi_get_named_property(env, step, "value", &tuple); + napi_value keyValue; + napi_value elementValue; + napi_get_element(env, tuple, 0, &keyValue); + napi_get_element(env, tuple, 1, &elementValue); + + id keyObject = nil; + id valueObject = nil; + toNative(env, keyValue, (void*)&keyObject, shouldFree, shouldFreeAny); + toNative(env, elementValue, (void*)&valueObject, shouldFree, shouldFreeAny); + + if (keyObject != nil && valueObject != nil) { + [(*res) setObject:valueObject forKey:keyObject]; + } + } + + cacheRoundTrip(*res); return; } if (!isEqual) { - auto bridgeState = ObjCBridgeState::InstanceData(env); *res = jsObjectToId(env, value); return; } + bool hasLength = false; + napi_has_named_property(env, value, "length", &hasLength); + if (hasLength) { + napi_value lengthValue; + napi_get_named_property(env, value, "length", &lengthValue); + napi_valuetype lengthType = napi_undefined; + napi_typeof(env, lengthValue, &lengthType); + if (lengthType == napi_number) { + uint32_t len = 0; + napi_get_value_uint32(env, lengthValue, &len); + *res = [NSMutableArray arrayWithCapacity:len]; + for (uint32_t i = 0; i < len; i++) { + bool hasElement = false; + napi_has_element(env, value, i, &hasElement); + if (!hasElement) { + [(*res) addObject:[NSNull null]]; + continue; + } + napi_value elem; + napi_get_element(env, value, i, &elem); + id obj = nil; + toNative(env, elem, (void*)&obj, shouldFree, shouldFreeAny); + [(*res) addObject:obj != nil ? obj : [NSNull null]]; + } + cacheRoundTrip(*res); + return; + } + } + *res = [NSMutableDictionary dictionary]; napi_value keys; napi_get_property_names(env, value, &keys); @@ -1268,10 +2446,44 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, if (obj != nil) [(*res) setObject:obj forKey:[NSString stringWithUTF8String:buf]]; } + cacheRoundTrip(*res); return; } } + if (bridgeState != nullptr && wrapped != nullptr) { + for (const auto& entry : bridgeState->classes) { + auto bridgedClass = entry.second; + if (bridgedClass == wrapped) { + *res = (id)bridgedClass->nativeClass; + return; + } + } + + for (const auto& entry : bridgeState->protocols) { + auto bridgedProtocol = entry.second; + if (bridgedProtocol != wrapped) { + continue; + } + + Protocol* runtimeProtocol = objc_getProtocol(bridgedProtocol->name.c_str()); + if (runtimeProtocol == nil) { + std::string baseName; + if (stripProtocolSuffix(bridgedProtocol->name.c_str(), &baseName)) { + runtimeProtocol = objc_getProtocol(baseName.c_str()); + } + } + + if (runtimeProtocol != nil) { + *res = (id)runtimeProtocol; + return; + } + } + } + + *res = (id)wrapped; + return; + break; } @@ -1306,7 +2518,10 @@ void free(napi_env env, void* value) override { class ObjCNSStringObjectTypeConv : public TypeConv { public: - ObjCNSStringObjectTypeConv() { type = &ffi_type_pointer; } + ObjCNSStringObjectTypeConv() { + type = &ffi_type_pointer; + kind = mdTypeNSStringObject; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { NSString* str = *((NSString**)value); @@ -1317,8 +2532,13 @@ napi_value toJS(napi_env env, void* value, uint32_t flags) override { return null; } + NSUInteger length = [str length]; + std::vector chars(length > 0 ? length : 1); + if (length > 0) { + [str getCharacters:(unichar*)chars.data() range:NSMakeRange(0, length)]; + } napi_value result; - napi_create_string_utf8(env, [str UTF8String], [str length], &result); + napi_create_string_utf16(env, length > 0 ? chars.data() : nullptr, length, &result); return result; } @@ -1336,10 +2556,13 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class ObjCNSMutableStringObjectTypeConv : public TypeConv { public: - ObjCNSMutableStringObjectTypeConv() { type = &ffi_type_pointer; } + ObjCNSMutableStringObjectTypeConv() { + type = &ffi_type_pointer; + kind = mdTypeNSMutableStringObject; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { - NSString* str = *((NSString**)value); + NSMutableString* str = *((NSMutableString**)value); if (str == nullptr) { napi_value null; @@ -1347,9 +2570,13 @@ napi_value toJS(napi_env env, void* value, uint32_t flags) override { return null; } - napi_value result; - napi_create_string_utf8(env, [str UTF8String], [str length], &result); - return result; + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (napi_value existing = bridgeState->findCachedObjectWrapper(env, str); existing != nullptr) { + return existing; + } + + ObjectOwnership ownership = (flags & kReturnOwned) != 0 ? kOwnedObject : kUnownedObject; + return bridgeState->getObject(env, str, ownership); } void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, @@ -1362,24 +2589,19 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, NSMutableString** res = (NSMutableString**)result; size_t len = 0; - NAPI_GUARD(napi_get_value_string_utf8(env, value, nullptr, len, &len)) { + NAPI_GUARD(napi_get_value_string_utf16(env, value, nullptr, len, &len)) { NAPI_THROW_LAST_ERROR return; } - char* str = (char*)malloc(len + 1); + std::vector chars(len + 1); - NAPI_GUARD(napi_get_value_string_utf8(env, value, str, len + 1, &len)) { + NAPI_GUARD(napi_get_value_string_utf16(env, value, chars.data(), len + 1, &len)) { NAPI_THROW_LAST_ERROR - ::free(str); return; } - str[len] = '\0'; - - *res = [[NSMutableString alloc] initWithUTF8String:str]; - - ::free(str); + *res = [[NSMutableString alloc] initWithCharacters:(unichar*)chars.data() length:len]; return; } @@ -1395,7 +2617,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class ObjCClassTypeConv : public TypeConv { public: - ObjCClassTypeConv() { type = &ffi_type_pointer; } + ObjCClassTypeConv() { + type = &ffi_type_pointer; + kind = mdTypeClass; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { Class cls = *((Class*)value); @@ -1437,7 +2662,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, class SelectorTypeConv : public TypeConv { public: - SelectorTypeConv() { type = &ffi_type_pointer; } + SelectorTypeConv() { + type = &ffi_type_pointer; + kind = mdTypeSelector; + } napi_value toJS(napi_env env, void* value, uint32_t flags) override { napi_value result; @@ -1491,6 +2719,7 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, StructTypeConv(MDSectionOffset structOffset, ffi_type* type) : structOffset(structOffset) { this->type = type; + kind = mdTypeStruct; } // ~StructTypeConv() { delete type; } @@ -1505,6 +2734,15 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, return info; } + inline size_t getStructSize(napi_env env) { + if (this->type != nullptr && this->type->size > 0) { + return this->type->size; + } + + auto info = getInfo(env); + return info != nullptr ? info->size : 0; + } + napi_value toJS(napi_env env, void* value, uint32_t flags) override { auto info = getInfo(env); @@ -1523,6 +2761,12 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, bool* shouldFreeAny) override { NAPI_PREAMBLE + const size_t structSize = getStructSize(env); + if (structSize == 0) { + napi_throw_type_error(env, "TypeError", "Invalid struct size"); + return; + } + bool isTypedArray = false; napi_is_typedarray(env, value, &isTypedArray); @@ -1534,8 +2778,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, NAPI_THROW_LAST_ERROR return; } - - memcpy(result, data, length * getTypedArrayUnitLength(type)); + const size_t unitLength = getTypedArrayUnitLength(type); + const size_t byteLength = length * unitLength; + memset(result, 0, structSize); + memcpy(result, data, std::min(byteLength, structSize)); return; } @@ -1564,7 +2810,9 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, auto structObject = StructObject::unwrap(env, value); if (structObject != nullptr) { - memcpy(result, structObject->data, structObject->info->size); + const size_t copySize = std::min(static_cast(structObject->info->size), structSize); + memset(result, 0, structSize); + memcpy(result, structObject->data, copySize); return; } @@ -1576,7 +2824,14 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, return; } - // Serialize directly to previously allocated memory + if (structSize < info->size) { + std::vector storage(info->size, 0); + StructObject(env, info, value, storage.data()); + memcpy(result, storage.data(), structSize); + return; + } + + // Serialize directly to previously allocated memory. StructObject(env, info, value, result); } }; @@ -1585,17 +2840,57 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, public: int arraySize; std::shared_ptr elementType; + bool decayToPointerForArguments = false; ArrayTypeConv(int arraySize, std::shared_ptr elementType) : arraySize(arraySize), elementType(elementType) { - type = &ffi_type_pointer; + auto arrayType = new ffi_type(); + arrayType->type = FFI_TYPE_STRUCT; + arrayType->size = 0; + arrayType->alignment = 0; + arrayType->elements = (ffi_type**)malloc(sizeof(ffi_type*) * (arraySize + 1)); + for (int i = 0; i < arraySize; i++) { + arrayType->elements[i] = elementType->type; + } + arrayType->elements[arraySize] = nullptr; + type = arrayType; + kind = mdTypeArray; + } + + ffi_type* ffiTypeForArgument() override { + decayToPointerForArguments = true; + return &ffi_type_pointer; } napi_value toJS(napi_env env, void* value, uint32_t flags) override { + if (decayToPointerForArguments) { + void* raw = *((void**)value); + if (raw == nullptr) { + napi_value nullValue; + napi_get_null(env, &nullValue); + return nullValue; + } + return Pointer::create(env, raw); + } + napi_value result; - void* data; - napi_create_arraybuffer(env, type->size, &data, &result); - memcpy(data, value, type->size); + napi_create_array_with_length(env, arraySize, &result); + + size_t elementSize = getElementSize(); + + auto base = static_cast(value); + for (int i = 0; i < arraySize; i++) { + void* slot = base + (i * elementSize); + napi_value elementValue = elementType->toJS(env, slot, flags); + napi_valuetype elementValueType = napi_undefined; + napi_typeof(env, elementValue, &elementValueType); + if (elementValueType == napi_boolean) { + bool boolValue = false; + napi_get_value_bool(env, elementValue, &boolValue); + napi_create_uint32(env, boolValue ? 1 : 0, &elementValue); + } + napi_set_element(env, result, i, elementValue); + } return result; } @@ -1603,50 +2898,340 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, bool* shouldFreeAny) override { NAPI_PREAMBLE + if (decayToPointerForArguments) { + void** pointerResult = static_cast(result); + *pointerResult = nullptr; + + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType == napi_null || valueType == napi_undefined) { + return; + } + + if (Pointer::isInstance(env, value)) { + Pointer* ptr = Pointer::unwrap(env, value); + *pointerResult = ptr != nullptr ? ptr->data : nullptr; + return; + } + + if (Reference::isInstance(env, value)) { + Reference* ref = Reference::unwrap(env, value); + *pointerResult = ref != nullptr ? ref->data : nullptr; + return; + } + + if (StructObject::isInstance(env, value)) { + StructObject* structObject = StructObject::unwrap(env, value); + *pointerResult = structObject != nullptr ? structObject->data : nullptr; + return; + } + + size_t arrayByteSize = getArrayByteSize(); + if (arrayByteSize == 0) { + return; + } + void* copiedBuffer = malloc(arrayByteSize); + if (copiedBuffer == nullptr) { + napi_throw_error(env, nullptr, "Out of memory while converting C array argument"); + return; + } + + copyToInlineArrayStorage(env, value, copiedBuffer, shouldFree, shouldFreeAny); + + bool hasPendingException = false; + napi_is_exception_pending(env, &hasPendingException); + if (hasPendingException) { + ::free(copiedBuffer); + return; + } + + *pointerResult = copiedBuffer; + if (shouldFree != nullptr) { + *shouldFree = true; + } + if (shouldFreeAny != nullptr) { + *shouldFreeAny = true; + } + return; + } + + copyToInlineArrayStorage(env, value, result, shouldFree, shouldFreeAny); + } + + void free(napi_env env, void* value) override { + if (value != nullptr) { + ::free(value); + } + } + + void encode(std::string* encoding) override { + *encoding += "["; + *encoding += std::to_string(arraySize); + elementType->encode(encoding); + *encoding += "]"; + } + + private: + size_t getElementSize() const { + size_t elementSize = + elementType != nullptr && elementType->type != nullptr ? elementType->type->size : 0; + if (elementSize == 0 && type != nullptr && arraySize > 0 && type->size >= (size_t)arraySize) { + elementSize = type->size / static_cast(arraySize); + } + if (elementSize == 0) { + elementSize = sizeof(void*); + } + return elementSize; + } + + size_t getArrayByteSize() const { return getElementSize() * static_cast(arraySize); } + + void copyToInlineArrayStorage(napi_env env, napi_value value, void* result, bool* shouldFree, + bool* shouldFreeAny) { + size_t elementSize = getElementSize(); + size_t arrayByteSize = getArrayByteSize(); + memset(result, 0, arrayByteSize); + + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType == napi_null || valueType == napi_undefined) { + return; + } + if (Pointer::isInstance(env, value)) { Pointer* ptr = Pointer::unwrap(env, value); - memcpy(result, ptr->data, type->size); + if (ptr == nullptr || ptr->data == nullptr) { + return; + } + memcpy(result, ptr->data, arrayByteSize); + return; + } + + bool isArray = false; + napi_is_array(env, value, &isArray); + if (isArray) { + for (int i = 0; i < arraySize; i++) { + bool hasElement = false; + napi_has_element(env, value, i, &hasElement); + if (!hasElement) { + continue; + } + + napi_value elementValue; + napi_get_element(env, value, i, &elementValue); + void* slot = static_cast(result) + (i * elementSize); + elementType->toNative(env, elementValue, slot, shouldFree, shouldFreeAny); + } + return; + } + + bool isArrayBuffer = false; + napi_is_arraybuffer(env, value, &isArrayBuffer); + if (isArrayBuffer) { + void* data = nullptr; + size_t byteLength = 0; + napi_get_arraybuffer_info(env, value, &data, &byteLength); + memcpy(result, data, std::min(byteLength, arrayByteSize)); return; } void* data; size_t length = 0; - napi_typedarray_type type; - NAPI_GUARD(napi_get_typedarray_info(env, value, &type, &length, &data, nullptr, nullptr)) { + napi_typedarray_type typedArrayType; + napi_status typedArrayStatus = + napi_get_typedarray_info(env, value, &typedArrayType, &length, &data, nullptr, nullptr); + if (typedArrayStatus != napi_ok) { NAPI_THROW_LAST_ERROR return; } - memcpy(result, data, length * getTypedArrayUnitLength(type)); - } - - void encode(std::string* encoding) override { - *encoding += "["; - *encoding += arraySize; - elementType->encode(encoding); - *encoding += "]"; + size_t copyLength = length * getTypedArrayUnitLength(typedArrayType); + memcpy(result, data, std::min(copyLength, arrayByteSize)); } }; class VectorTypeConv : public TypeConv { public: - // TypeConv elementType; - - VectorTypeConv() { - // TODO - type = &ffi_type_pointer; + uint16_t vectorSize; + std::shared_ptr elementType; + MDTypeKind vectorKind; + + VectorTypeConv(MDTypeKind vectorKind, uint16_t vectorSize, std::shared_ptr elementType) + : vectorSize(vectorSize), elementType(elementType), vectorKind(vectorKind) { + auto vectorType = new ffi_type(); +#if defined(FFI_TYPE_EXT_VECTOR) + vectorType->type = vectorKind == mdTypeComplex ? FFI_TYPE_COMPLEX : FFI_TYPE_EXT_VECTOR; +#else + vectorType->type = vectorKind == mdTypeComplex ? FFI_TYPE_COMPLEX : FFI_TYPE_STRUCT; +#endif + size_t lanes = std::max(vectorSize, 1); + // 3-lane vectors are ABI-lowered to 4-lane storage on Apple platforms. + size_t abiLanes = lanes == 3 ? 4 : lanes; + vectorType->elements = (ffi_type**)malloc(sizeof(ffi_type*) * (abiLanes + 1)); + + ffi_type* elementFfiType = elementType != nullptr && elementType->type != nullptr + ? elementType->type + : &ffi_type_float; + const size_t elementSize = std::max(elementFfiType->size, sizeof(float)); + const size_t elementAlignment = + std::max(elementFfiType->alignment, static_cast(1)); + + for (size_t i = 0; i < abiLanes; i++) { + vectorType->elements[i] = elementFfiType; + } + vectorType->elements[abiLanes] = nullptr; + + size_t vectorAlignment = elementAlignment; + if (vectorKind != mdTypeComplex) { + size_t packedSize = abiLanes * elementSize; + size_t preferredAlignment = packedSize >= 16 ? 16 : packedSize; + vectorAlignment = std::max(vectorAlignment, preferredAlignment); + } + vectorAlignment = std::min(vectorAlignment, 16); + vectorType->alignment = static_cast(vectorAlignment); + vectorType->size = alignUp(abiLanes * elementSize, vectorAlignment); + + type = vectorType; + kind = vectorKind; } napi_value toJS(napi_env env, void* value, uint32_t flags) override { - NSLog(@"VectorTypeConv toJS: TODO"); napi_value result; - napi_get_null(env, &result); + napi_create_array_with_length(env, vectorSize, &result); + + size_t elementSize = getElementSize(); + auto base = static_cast(value); + for (uint16_t i = 0; i < vectorSize; i++) { + void* slot = base + (static_cast(i) * elementSize); + napi_value elementValue = elementType->toJS(env, slot, flags); + napi_valuetype elementValueType = napi_undefined; + napi_typeof(env, elementValue, &elementValueType); + if (elementValueType == napi_boolean) { + bool boolValue = false; + napi_get_value_bool(env, elementValue, &boolValue); + napi_create_uint32(env, boolValue ? 1 : 0, &elementValue); + } + napi_set_element(env, result, i, elementValue); + } return result; } void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, bool* shouldFreeAny) override { - NSLog(@"VectorTypeConv toNative: TODO"); + NAPI_PREAMBLE + + memset(result, 0, getVectorByteSize()); + + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType == napi_null || valueType == napi_undefined) { + return; + } + + if (Pointer::isInstance(env, value)) { + Pointer* ptr = Pointer::unwrap(env, value); + if (ptr != nullptr && ptr->data != nullptr) { + copyFromContiguousBuffer(ptr->data, getVectorByteSize(), result); + } + return; + } + + if (Reference::isInstance(env, value)) { + Reference* ref = Reference::unwrap(env, value); + if (ref != nullptr && ref->data != nullptr) { + copyFromContiguousBuffer(ref->data, getVectorByteSize(), result); + } + return; + } + + if (StructObject::isInstance(env, value)) { + StructObject* structObject = StructObject::unwrap(env, value); + if (structObject != nullptr && structObject->data != nullptr) { + copyFromContiguousBuffer(structObject->data, structObject->info->size, result); + } + return; + } + + bool isArray = false; + napi_is_array(env, value, &isArray); + if (isArray) { + writeFromArrayElements(env, value, result, shouldFree, shouldFreeAny); + return; + } + + bool isArrayBuffer = false; + napi_is_arraybuffer(env, value, &isArrayBuffer); + if (isArrayBuffer) { + void* data = nullptr; + size_t byteLength = 0; + napi_get_arraybuffer_info(env, value, &data, &byteLength); + copyFromContiguousBuffer(data, byteLength, result); + return; + } + + void* data = nullptr; + size_t length = 0; + napi_typedarray_type typedArrayType = napi_int8_array; + napi_status typedArrayStatus = + napi_get_typedarray_info(env, value, &typedArrayType, &length, &data, nullptr, nullptr); + if (typedArrayStatus == napi_ok) { + size_t copyLength = length * getTypedArrayUnitLength(typedArrayType); + copyFromContiguousBuffer(data, copyLength, result); + return; + } + + napi_throw_type_error( + env, "TypeError", + "Invalid vector type, expected array, typed array, array buffer, pointer or reference."); + } + + void encode(std::string* encoding) override { + *encoding += "V"; + *encoding += std::to_string(vectorSize); + if (elementType != nullptr) { + elementType->encode(encoding); + } + } + + private: + inline size_t getElementSize() const { + size_t elementSize = + elementType != nullptr && elementType->type != nullptr ? elementType->type->size : 0; + if (elementSize == 0) { + elementSize = sizeof(float); + } + return elementSize; + } + + inline size_t getVectorByteSize() const { + size_t expectedSize = static_cast(vectorSize) * getElementSize(); + if (type != nullptr && type->size > expectedSize) { + expectedSize = type->size; + } + return expectedSize; + } + + inline void copyFromContiguousBuffer(void* source, size_t sourceLength, void* destination) const { + memcpy(destination, source, std::min(sourceLength, getVectorByteSize())); + } + + void writeFromArrayElements(napi_env env, napi_value value, void* result, bool* shouldFree, + bool* shouldFreeAny) { + size_t elementSize = getElementSize(); + auto base = static_cast(result); + + for (uint16_t i = 0; i < vectorSize; i++) { + bool hasElement = false; + napi_has_element(env, value, i, &hasElement); + if (!hasElement) { + continue; + } + + napi_value elementValue; + napi_get_element(env, value, i, &elementValue); + void* slot = base + (static_cast(i) * elementSize); + elementType->toNative(env, elementValue, slot, shouldFree, shouldFreeAny); + } } }; @@ -1727,10 +3312,19 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, case '{': { std::string structname; const char* c = *encoding + 1; - while (*c != '=') { + while (*c != '\0' && *c != '=') { structname += *c; c++; } + if (*c != '=') { + while (**encoding != '\0' && **encoding != '}') { + (*encoding)++; + } + if (**encoding == '}') { + (*encoding)++; + } + return pointerTypeConv; + } // Check if we already have a cached StructTypeConv for this encoding-based struct auto cacheIt = encodingStructCache.find(structname); @@ -1739,9 +3333,13 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, } auto bridgeState = ObjCBridgeState::InstanceData(env); - // NSLog(@"struct: %s, %d", structname.c_str(), - // bridgeState->structOffsets[structname]); - auto structOffset = bridgeState->structOffsets[structname]; + MDSectionOffset structOffset = MD_SECTION_OFFSET_NULL; + if (bridgeState != nullptr) { + auto structOffsetIt = bridgeState->structOffsets.find(structname); + if (structOffsetIt != bridgeState->structOffsets.end()) { + structOffset = structOffsetIt->second; + } + } auto type = typeFromStruct(env, encoding); auto structTypeConv = std::make_shared(StructTypeConv(structOffset, type)); @@ -1796,7 +3394,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, return sint64TypeConv; } - case mdTypeUInt8: + case mdTypeUInt8: { + return uint8TypeConv; + } + case mdTypeUChar: { return ucharTypeConv; } @@ -1952,8 +3553,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, } case mdTypeVector: { - // TODO - return std::make_shared(); + auto vectorSize = reader->getArraySize(*offset); + *offset += sizeof(uint16_t); + auto elementType = TypeConv::Make(env, reader, offset, opaquePointers); + return std::make_shared(kind, vectorSize, elementType); } case mdTypeBlock: { @@ -1973,13 +3576,17 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, } case mdTypeExtVector: { - // TODO - return std::make_shared(); + auto vectorSize = reader->getArraySize(*offset); + *offset += sizeof(uint16_t); + auto elementType = TypeConv::Make(env, reader, offset, opaquePointers); + return std::make_shared(kind, vectorSize, elementType); } case mdTypeComplex: { - // TODO - return std::make_shared(); + auto vectorSize = reader->getArraySize(*offset); + *offset += sizeof(uint16_t); + auto elementType = TypeConv::Make(env, reader, offset, opaquePointers); + return std::make_shared(kind, vectorSize, elementType); } default: @@ -1988,6 +3595,342 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree, } } +namespace { + +bool tryFastConvertStringToNSString(napi_env env, napi_value value, id* out, bool mutableString) { + if (out == nullptr) { + return false; + } + + if (mutableString) { + constexpr size_t kStackUtf16Capacity = 128; + char16_t utf16Stack[kStackUtf16Capacity]; + char16_t* utf16Buffer = utf16Stack; + size_t utf16Capacity = kStackUtf16Capacity; + size_t utf16Length = 0; + if (napi_get_value_string_utf16(env, value, utf16Buffer, utf16Capacity, &utf16Length) != + napi_ok) { + return false; + } + + std::vector utf16Heap; + if (utf16Length + 1 >= utf16Capacity) { + if (napi_get_value_string_utf16(env, value, nullptr, 0, &utf16Length) != napi_ok) { + return false; + } + utf16Heap.resize(utf16Length + 1, 0); + utf16Buffer = utf16Heap.data(); + utf16Capacity = utf16Heap.size(); + if (napi_get_value_string_utf16(env, value, utf16Buffer, utf16Capacity, &utf16Length) != + napi_ok) { + return false; + } + } + + *out = [[NSMutableString alloc] initWithCharacters:reinterpret_cast(utf16Buffer) + length:utf16Length]; + return true; + } + + constexpr size_t kStackUtf8Capacity = 256; + char utf8Stack[kStackUtf8Capacity]; + char* utf8Buffer = utf8Stack; + size_t utf8Capacity = kStackUtf8Capacity; + size_t utf8Length = 0; + if (napi_get_value_string_utf8(env, value, utf8Buffer, utf8Capacity, &utf8Length) != napi_ok) { + return false; + } + + std::vector utf8Heap; + if (utf8Length + 1 >= utf8Capacity) { + if (napi_get_value_string_utf8(env, value, nullptr, 0, &utf8Length) != napi_ok) { + return false; + } + utf8Heap.resize(utf8Length + 1, '\0'); + utf8Buffer = utf8Heap.data(); + utf8Capacity = utf8Heap.size(); + if (napi_get_value_string_utf8(env, value, utf8Buffer, utf8Capacity, &utf8Length) != napi_ok) { + return false; + } + } + + id stringValue = [[[NSString alloc] initWithBytes:utf8Buffer + length:utf8Length + encoding:NSUTF8StringEncoding] autorelease]; + *out = stringValue != nil ? stringValue : [NSString string]; + return true; +} + +bool tryFastConvertObjCObjectValue(napi_env env, napi_value value, napi_valuetype valueType, + MDTypeKind kind, id* out) { + if (out == nullptr) { + return false; + } + + switch (valueType) { + case napi_null: + case napi_undefined: + *out = nil; + return true; + + case napi_external: { + void* external = nullptr; + if (napi_get_value_external(env, value, &external) != napi_ok) { + return false; + } + *out = static_cast(external); + return true; + } + + case napi_number: { + double numericValue = 0; + if (napi_get_value_double(env, value, &numericValue) != napi_ok) { + return false; + } + *out = [NSNumber numberWithDouble:numericValue]; + return true; + } + + case napi_boolean: { + bool boolValue = false; + if (napi_get_value_bool(env, value, &boolValue) != napi_ok) { + return false; + } + *out = [NSNumber numberWithBool:boolValue]; + return true; + } + + case napi_bigint: { + int64_t bigintValue = 0; + bool lossless = false; + if (napi_get_value_bigint_int64(env, value, &bigintValue, &lossless) != napi_ok) { + return false; + } + *out = [NSNumber numberWithLongLong:bigintValue]; + return true; + } + + case napi_string: + return tryFastConvertStringToNSString(env, value, out, kind == mdTypeNSMutableStringObject); + + case napi_object: + case napi_function: { + auto bridgeState = ObjCBridgeState::InstanceData(env); + auto cacheRoundTrip = [&](id nativeObj) { + if (nativeObj == nil || bridgeState == nullptr || !bridgeState->hasRoundTripCacheFrame()) { + return; + } + + bridgeState->cacheRoundTripObject(env, nativeObj, value); + }; + + if (valueType == napi_object) { + if (Pointer::isInstance(env, value)) { + Pointer* ptr = Pointer::unwrap(env, value); + void* pointerData = ptr != nullptr ? ptr->data : nullptr; + if (id cachedObject = resolveCachedHandleObject(env, pointerData); cachedObject != nil) { + *out = cachedObject; + return true; + } + *out = (id)pointerData; + return true; + } + if (Reference::isInstance(env, value)) { + Reference* ref = Reference::unwrap(env, value); + void* referenceData = ref != nullptr ? ref->data : nullptr; + if (id cachedObject = resolveCachedHandleObject(env, referenceData); + cachedObject != nil) { + *out = cachedObject; + return true; + } + *out = (id)referenceData; + return true; + } + } + + void* wrapped = nullptr; + if (napi_unwrap(env, value, &wrapped) == napi_ok) { + if (valueType == napi_function) { + auto bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState != nullptr && wrapped != nullptr) { + for (const auto& entry : bridgeState->classes) { + auto bridgedClass = entry.second; + if (bridgedClass == wrapped) { + *out = (id)bridgedClass->nativeClass; + return true; + } + } + + for (const auto& entry : bridgeState->protocols) { + auto bridgedProtocol = entry.second; + if (bridgedProtocol == wrapped) { + Protocol* runtimeProtocol = objc_getProtocol(bridgedProtocol->name.c_str()); + if (runtimeProtocol == nil) { + std::string baseName; + if (stripProtocolSuffix(bridgedProtocol->name.c_str(), &baseName)) { + runtimeProtocol = objc_getProtocol(baseName.c_str()); + } + } + if (runtimeProtocol != nil) { + *out = (id)runtimeProtocol; + return true; + } + } + } + } + } + + *out = (id)wrapped; + return true; + } + + bool isTypedArray = false; + if (napi_is_typedarray(env, value, &isTypedArray) == napi_ok && isTypedArray) { + *out = createNSDataWrapper(env, value, bridgeState); + if (*out != nil) { + cacheRoundTrip(*out); + return true; + } + return false; + } + + bool isArrayBuffer = false; + if (napi_is_arraybuffer(env, value, &isArrayBuffer) == napi_ok && isArrayBuffer) { + *out = createNSDataWrapper(env, value, bridgeState); + if (*out != nil) { + cacheRoundTrip(*out); + return true; + } + return false; + } + + bool isDataView = false; + if (napi_is_dataview(env, value, &isDataView) == napi_ok && isDataView) { + *out = createNSDataWrapper(env, value, bridgeState); + if (*out != nil) { + cacheRoundTrip(*out); + return true; + } + return false; + } + + return false; + } + + default: + return false; + } +} + +} // namespace + +bool TryFastConvertNapiArgument(napi_env env, MDTypeKind kind, napi_value value, void* result) { + if (result == nullptr || value == nullptr) { + return false; + } + + napi_valuetype valueType = napi_undefined; + if (napi_typeof(env, value, &valueType) != napi_ok) { + return false; + } + + switch (kind) { + case mdTypeAnyObject: + case mdTypeProtocolObject: + case mdTypeClassObject: + case mdTypeInstanceObject: + case mdTypeNSStringObject: + case mdTypeNSMutableStringObject: + return tryFastConvertObjCObjectValue(env, value, valueType, static_cast(kind), + reinterpret_cast(result)); + + case mdTypeSelector: { + SEL* selector = reinterpret_cast(result); + switch (valueType) { + case napi_null: + case napi_undefined: + *selector = nullptr; + return true; + + case napi_string: { + constexpr size_t kStackSelectorCapacity = 128; + char selectorStack[kStackSelectorCapacity]; + size_t selectorLength = 0; + if (napi_get_value_string_utf8(env, value, selectorStack, kStackSelectorCapacity, + &selectorLength) != napi_ok) { + return false; + } + const char* selectorName = selectorStack; + std::vector selectorHeap; + if (selectorLength + 1 >= kStackSelectorCapacity) { + if (napi_get_value_string_utf8(env, value, nullptr, 0, &selectorLength) != napi_ok) { + return false; + } + selectorHeap.resize(selectorLength + 1, '\0'); + if (napi_get_value_string_utf8(env, value, selectorHeap.data(), selectorHeap.size(), + &selectorLength) != napi_ok) { + return false; + } + selectorName = selectorHeap.data(); + } + *selector = sel_registerName(selectorName); + return true; + } + + default: + return false; + } + } + + default: + return false; + } +} + +bool TryFastConvertNapiUInt16Argument(napi_env env, napi_value value, uint16_t* result) { + if (result == nullptr || value == nullptr) { + return false; + } + + napi_valuetype valueType = napi_undefined; + if (napi_typeof(env, value, &valueType) != napi_ok) { + return false; + } + + if (valueType == napi_string) { + size_t strLen = 0; + if (napi_get_value_string_utf16(env, value, nullptr, 0, &strLen) != napi_ok) { + return false; + } + if (strLen != 1) { + napi_throw_type_error(env, nullptr, "Expected a single-character string."); + *result = 0; + return false; + } + + char16_t chars[2] = {0, 0}; + if (napi_get_value_string_utf16(env, value, chars, 2, &strLen) != napi_ok) { + return false; + } + + *result = static_cast(chars[0]); + return true; + } + + napi_value coerced = value; + if (napi_coerce_to_number(env, value, &coerced) != napi_ok) { + return false; + } + + uint32_t converted = 0; + if (napi_get_value_uint32(env, coerced, &converted) != napi_ok) { + return false; + } + + *result = static_cast(converted); + return true; +} + // Cleanup function to clear thread-local caches void clearStructTypeCaches() { processingStructs.clear(); diff --git a/NativeScript/ffi/Util.mm b/NativeScript/ffi/Util.mm index f56a3bea..a4bd1c2a 100644 --- a/NativeScript/ffi/Util.mm +++ b/NativeScript/ffi/Util.mm @@ -1,5 +1,7 @@ #include "Util.h" +#include #include "Metadata.h" +#include "ObjCBridge.h" #include "js_native_api.h" #include "js_native_api_types.h" @@ -7,6 +9,78 @@ namespace nativescript { +inline std::string getStructEncodingForValue(napi_env env, napi_value value) { + ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState == nullptr || value == nullptr) { + return ""; + } + + napi_valuetype valueType = napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType != napi_function && valueType != napi_object) { + return ""; + } + + napi_value typeSymbol = jsSymbolFor(env, "type"); + if (typeSymbol != nullptr) { + bool hasTypeEncoding = false; + if (napi_has_property(env, value, typeSymbol, &hasTypeEncoding) == napi_ok && hasTypeEncoding) { + napi_value typeEncodingValue = nullptr; + if (napi_get_property(env, value, typeSymbol, &typeEncodingValue) == napi_ok && + typeEncodingValue != nullptr) { + napi_valuetype typeEncodingType = napi_undefined; + napi_typeof(env, typeEncodingValue, &typeEncodingType); + if (typeEncodingType == napi_string) { + size_t length = 0; + napi_get_value_string_utf8(env, typeEncodingValue, nullptr, 0, &length); + std::vector buffer(length + 1, '\0'); + if (napi_get_value_string_utf8(env, typeEncodingValue, buffer.data(), buffer.size(), + &length) == napi_ok) { + return std::string(buffer.data(), length); + } + } + } + } + } + + if (!bridgeState->structOffsets.empty()) { + napi_value nameValue = nullptr; + if (napi_get_named_property(env, value, "name", &nameValue) == napi_ok && + nameValue != nullptr) { + napi_valuetype nameType = napi_undefined; + napi_typeof(env, nameValue, &nameType); + if (nameType == napi_string) { + size_t nameLength = 0; + napi_get_value_string_utf8(env, nameValue, nullptr, 0, &nameLength); + std::vector nameBuffer(nameLength + 1, '\0'); + if (napi_get_value_string_utf8(env, nameValue, nameBuffer.data(), nameBuffer.size(), + &nameLength) == napi_ok) { + std::string candidateName(nameBuffer.data(), nameLength); + auto structIt = bridgeState->structOffsets.find(candidateName); + if (structIt != bridgeState->structOffsets.end()) { + StructInfo* info = bridgeState->getStructInfo(env, structIt->second); + if (info != nullptr && info->name != nullptr) { + std::string encoding = "{"; + encoding += info->name; + encoding += "="; + for (const auto& field : info->fields) { + if (field.type == nullptr) { + return ""; + } + field.type->encode(&encoding); + } + encoding += "}"; + return encoding; + } + } + } + } + } + } + + return ""; +} + std::string implicitSetterSelector(std::string name) { std::string setter; setter += "set"; @@ -109,8 +183,20 @@ napi_value jsSymbolFor(napi_env env, const char* string) { } case napi_function: - // Must be a native class constructor like NSObject. - return "@"; + case napi_object: { + std::string structEncoding = getStructEncodingForValue(env, value); + if (!structEncoding.empty()) { + return structEncoding; + } + + if (type == napi_function) { + // Native class constructor like NSObject. + return "@"; + } + + napi_throw_error(env, nullptr, "Invalid type"); + return "v"; + } default: napi_throw_error(env, nullptr, "Invalid type"); diff --git a/NativeScript/ffi/Variable.mm b/NativeScript/ffi/Variable.mm index 8ed8f6ff..69db80fa 100644 --- a/NativeScript/ffi/Variable.mm +++ b/NativeScript/ffi/Variable.mm @@ -5,26 +5,63 @@ namespace nativescript { +namespace { + +const char* getValidatedConstantString(MDMetadataReader* metadata, + MDSectionOffset stringOffsetRef) { + if (metadata == nullptr || metadata->data == nullptr) { + return nullptr; + } + + if (stringOffsetRef < metadata->constantsOffset || + stringOffsetRef + sizeof(MDSectionOffset) > metadata->enumsOffset) { + return nullptr; + } + + const auto stringsStart = static_cast(metadata->data) + metadata->stringsOffset; + const auto stringsSize = metadata->constantsOffset - metadata->stringsOffset; + if (stringsSize == 0) { + return nullptr; + } + + const auto stringOffset = metadata->getOffset(stringOffsetRef); + if (stringOffset >= stringsSize) { + return nullptr; + } + + const char* value = stringsStart + stringOffset; + const auto remaining = stringsSize - stringOffset; + if (memchr(value, '\0', remaining) == nullptr) { + return nullptr; + } + + return value; +} + +} // namespace + void ObjCBridgeState::registerVarGlobals(napi_env env, napi_value global) { auto offset = metadata->constantsOffset; while (offset < metadata->enumsOffset) { MDSectionOffset originalOffset = offset; - auto name = metadata->getString(offset); + auto name = getValidatedConstantString(metadata, offset); offset += sizeof(MDSectionOffset); auto evalKind = metadata->getVariableEvalKind(offset); offset += sizeof(MDVariableEvalKind); - napi_property_descriptor prop = { - .utf8name = name, - .method = nullptr, - .getter = JS_variableGetter, - .setter = nullptr, - .value = nullptr, - .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), - .data = (void*)((size_t)originalOffset), - }; - - napi_define_properties(env, global, 1, &prop); + if (name != nullptr && name[0] != '\0') { + napi_property_descriptor prop = { + .utf8name = name, + .method = nullptr, + .getter = JS_variableGetter, + .setter = nullptr, + .value = nullptr, + .attributes = (napi_property_attributes)(napi_enumerable | napi_configurable), + .data = (void*)((size_t)originalOffset), + }; + + napi_define_properties(env, global, 1, &prop); + } switch (evalKind) { case mdEvalDouble: { @@ -61,16 +98,20 @@ return get_ref_value(env, cached); } + napi_value result = nullptr; + // Name - auto name = bridgeState->metadata->getString(offset); + auto name = getValidatedConstantString(bridgeState->metadata, offset); + if (name == nullptr || name[0] == '\0') { + napi_get_null(env, &result); + return result; + } offset += sizeof(MDSectionOffset); // Eval kind auto evalKind = bridgeState->metadata->getVariableEvalKind(offset); offset += sizeof(MDVariableEvalKind); - napi_value result = nullptr; - switch (evalKind) { case mdEvalDouble: { auto value = bridgeState->metadata->getDouble(offset); @@ -96,6 +137,9 @@ default: { auto type = TypeConv::Make(env, bridgeState->metadata, &offset); auto value = dlsym(bridgeState->self_dl, name); + if (value == nullptr) { + value = dlsym(RTLD_DEFAULT, name); + } if (value == nullptr) { napi_get_null(env, &result); } else { diff --git a/NativeScript/ffi/node_api_util.h b/NativeScript/ffi/node_api_util.h index c8a63af5..8a1a0dc0 100644 --- a/NativeScript/ffi/node_api_util.h +++ b/NativeScript/ffi/node_api_util.h @@ -25,9 +25,8 @@ inline bool napiSupportsThreadsafeFunctions(void* dl) { return NULL; \ } -#define NAPI_ERROR_INFO \ - const napi_extended_error_info* error_info = \ - (napi_extended_error_info*)malloc(sizeof(napi_extended_error_info)); \ +#define NAPI_ERROR_INFO \ + const napi_extended_error_info* error_info = nullptr; \ napi_get_last_error_info(env, &error_info); #define NAPI_THROW_LAST_ERROR \ diff --git a/NativeScript/libffi/iphoneos-arm64/include/ffi.h b/NativeScript/libffi/iphoneos-arm64/include/ffi.h index 7ce4df03..c747f3b5 100644 --- a/NativeScript/libffi/iphoneos-arm64/include/ffi.h +++ b/NativeScript/libffi/iphoneos-arm64/include/ffi.h @@ -1,7 +1,6 @@ /* -----------------------------------------------------------------*-C-*- - libffi 3.4.4 - - Copyright (c) 2011, 2014, 2019, 2021, 2022 Anthony Green - - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. + libffi 3.3-rc0 - Copyright (c) 2011, 2014 Anthony Green + - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -56,31 +55,6 @@ extern "C" { /* ---- System configuration information --------------------------------- */ -/* If these change, update src/mips/ffitarget.h. */ -#define FFI_TYPE_VOID 0 -#define FFI_TYPE_INT 1 -#define FFI_TYPE_FLOAT 2 -#define FFI_TYPE_DOUBLE 3 -#if 0 -#define FFI_TYPE_LONGDOUBLE 4 -#else -#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE -#endif -#define FFI_TYPE_UINT8 5 -#define FFI_TYPE_SINT8 6 -#define FFI_TYPE_UINT16 7 -#define FFI_TYPE_SINT16 8 -#define FFI_TYPE_UINT32 9 -#define FFI_TYPE_SINT32 10 -#define FFI_TYPE_UINT64 11 -#define FFI_TYPE_SINT64 12 -#define FFI_TYPE_STRUCT 13 -#define FFI_TYPE_POINTER 14 -#define FFI_TYPE_COMPLEX 15 - -/* This should always refer to the last type code (for sanity checks). */ -#define FFI_TYPE_LAST FFI_TYPE_COMPLEX - #include #ifndef LIBFFI_ASM @@ -243,8 +217,7 @@ FFI_EXTERN ffi_type ffi_type_complex_longdouble; typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, - FFI_BAD_ABI, - FFI_BAD_ARGTYPE + FFI_BAD_ABI } ffi_status; typedef struct { @@ -315,15 +288,15 @@ FFI_API void ffi_java_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, - ffi_java_raw *avalue) __attribute__((deprecated)); + ffi_java_raw *avalue); #endif FFI_API -void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated)); +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw); FFI_API -void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __attribute__((deprecated)); +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args); FFI_API -size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); +size_t ffi_java_raw_size (ffi_cif *cif); /* ---- Definitions for closures ----------------------------------------- */ @@ -337,17 +310,11 @@ typedef struct { void *trampoline_table; void *trampoline_table_entry; #else - union { - char tramp[FFI_TRAMPOLINE_SIZE]; - void *ftramp; - }; + char tramp[FFI_TRAMPOLINE_SIZE]; #endif ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); void *user_data; -#if defined(_MSC_VER) && defined(_M_IX86) - void *padding; -#endif } ffi_closure #ifdef __GNUC__ __attribute__((aligned (8))) @@ -363,14 +330,6 @@ typedef struct { FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); -#if defined(PA_LINUX) || defined(PA_HPUX) -#define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) -#define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) -#else -#define FFI_CLOSURE_PTR(X) (X) -#define FFI_RESTORE_PTR(X) (X) -#endif - FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, @@ -388,7 +347,7 @@ ffi_prep_closure_loc (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, - void *codeloc); + void*codeloc); #ifdef __sgi # pragma pack 8 @@ -406,7 +365,7 @@ typedef struct { /* If this is enabled, then a raw closure has the same layout as a regular closure. We use this to install an intermediate - handler to do the translation, void** -> ffi_raw*. */ + handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void *this_closure; @@ -462,14 +421,14 @@ FFI_API ffi_status ffi_prep_java_raw_closure (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), - void *user_data) __attribute__((deprecated)); + void *user_data); FFI_API ffi_status ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data, - void *codeloc) __attribute__((deprecated)); + void *codeloc); #endif #endif /* FFI_CLOSURES */ @@ -524,6 +483,40 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #endif +/* If these change, update src/mips/ffitarget.h. */ +#define FFI_TYPE_VOID 0 +#define FFI_TYPE_INT 1 +#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_DOUBLE 3 +#if 0 +#define FFI_TYPE_LONGDOUBLE 4 +#else +#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE +#endif +#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_SINT8 6 +#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_SINT16 8 +#define FFI_TYPE_UINT32 9 +#define FFI_TYPE_SINT32 10 +#define FFI_TYPE_UINT64 11 +#define FFI_TYPE_SINT64 12 +#define FFI_TYPE_STRUCT 13 +#define FFI_TYPE_POINTER 14 +#define FFI_TYPE_COMPLEX 15 +#if 1 +#define FFI_TYPE_EXT_VECTOR 16 +#else +#define FFI_TYPE_EXT_VECTOR FFI_TYPE_STRUCT +#endif + +/* This should always refer to the last type code (for sanity checks). */ +#if 1 +#define FFI_TYPE_LAST FFI_TYPE_EXT_VECTOR +#else +#define FFI_TYPE_LAST FFI_TYPE_COMPLEX +#endif + #ifdef __cplusplus } #endif diff --git a/NativeScript/libffi/iphoneos-arm64/include/ffitarget.h b/NativeScript/libffi/iphoneos-arm64/include/ffitarget.h index d5622e13..ecb6d2de 100644 --- a/NativeScript/libffi/iphoneos-arm64/include/ffitarget.h +++ b/NativeScript/libffi/iphoneos-arm64/include/ffitarget.h @@ -32,7 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define FFI_SIZEOF_JAVA_RAW 4 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; -#elif defined(_WIN32) +#elif defined(_M_ARM64) #define FFI_SIZEOF_ARG 8 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; @@ -45,13 +45,8 @@ typedef enum ffi_abi { FFI_FIRST_ABI = 0, FFI_SYSV, - FFI_WIN64, FFI_LAST_ABI, -#if defined(_WIN32) - FFI_DEFAULT_ABI = FFI_WIN64 -#else FFI_DEFAULT_ABI = FFI_SYSV -#endif } ffi_abi; #endif @@ -74,22 +69,22 @@ typedef enum ffi_abi #define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE #endif -#ifdef _WIN32 +#ifdef _M_ARM64 #define FFI_EXTRA_CIF_FIELDS unsigned is_variadic #endif -#define FFI_TARGET_SPECIFIC_VARIADIC /* ---- Internal ---- */ #if defined (__APPLE__) +#define FFI_TARGET_SPECIFIC_VARIADIC #define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs -#elif !defined(_WIN32) +#elif !defined(_M_ARM64) /* iOS and Windows reserve x18 for the system. Disable Go closures until a new static chain is chosen. */ #define FFI_GO_CLOSURES 1 #endif -#ifndef _WIN32 +#ifndef _M_ARM64 /* No complex type on Windows */ #define FFI_TARGET_HAS_COMPLEX_TYPE #endif diff --git a/NativeScript/libffi/iphoneos-arm64/libffi.a b/NativeScript/libffi/iphoneos-arm64/libffi.a index 0b44615f..d22c9572 100644 Binary files a/NativeScript/libffi/iphoneos-arm64/libffi.a and b/NativeScript/libffi/iphoneos-arm64/libffi.a differ diff --git a/NativeScript/libffi/iphonesimulator-universal/include/ffi.h b/NativeScript/libffi/iphonesimulator-universal/include/ffi.h index 70689c5c..952b89fd 100644 --- a/NativeScript/libffi/iphonesimulator-universal/include/ffi.h +++ b/NativeScript/libffi/iphonesimulator-universal/include/ffi.h @@ -3,9 +3,8 @@ #if defined(__aarch64__) /* -----------------------------------------------------------------*-C-*- - libffi 3.4.4 - - Copyright (c) 2011, 2014, 2019, 2021, 2022 Anthony Green - - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. + libffi 3.3-rc0 - Copyright (c) 2011, 2014 Anthony Green + - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -60,31 +59,6 @@ extern "C" { /* ---- System configuration information --------------------------------- */ -/* If these change, update src/mips/ffitarget.h. */ -#define FFI_TYPE_VOID 0 -#define FFI_TYPE_INT 1 -#define FFI_TYPE_FLOAT 2 -#define FFI_TYPE_DOUBLE 3 -#if 0 -#define FFI_TYPE_LONGDOUBLE 4 -#else -#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE -#endif -#define FFI_TYPE_UINT8 5 -#define FFI_TYPE_SINT8 6 -#define FFI_TYPE_UINT16 7 -#define FFI_TYPE_SINT16 8 -#define FFI_TYPE_UINT32 9 -#define FFI_TYPE_SINT32 10 -#define FFI_TYPE_UINT64 11 -#define FFI_TYPE_SINT64 12 -#define FFI_TYPE_STRUCT 13 -#define FFI_TYPE_POINTER 14 -#define FFI_TYPE_COMPLEX 15 - -/* This should always refer to the last type code (for sanity checks). */ -#define FFI_TYPE_LAST FFI_TYPE_COMPLEX - #include #ifndef LIBFFI_ASM @@ -247,8 +221,7 @@ FFI_EXTERN ffi_type ffi_type_complex_longdouble; typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, - FFI_BAD_ABI, - FFI_BAD_ARGTYPE + FFI_BAD_ABI } ffi_status; typedef struct { @@ -319,15 +292,15 @@ FFI_API void ffi_java_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, - ffi_java_raw *avalue) __attribute__((deprecated)); + ffi_java_raw *avalue); #endif FFI_API -void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated)); +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw); FFI_API -void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __attribute__((deprecated)); +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args); FFI_API -size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); +size_t ffi_java_raw_size (ffi_cif *cif); /* ---- Definitions for closures ----------------------------------------- */ @@ -341,17 +314,11 @@ typedef struct { void *trampoline_table; void *trampoline_table_entry; #else - union { - char tramp[FFI_TRAMPOLINE_SIZE]; - void *ftramp; - }; + char tramp[FFI_TRAMPOLINE_SIZE]; #endif ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); void *user_data; -#if defined(_MSC_VER) && defined(_M_IX86) - void *padding; -#endif } ffi_closure #ifdef __GNUC__ __attribute__((aligned (8))) @@ -367,14 +334,6 @@ typedef struct { FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); -#if defined(PA_LINUX) || defined(PA_HPUX) -#define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) -#define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) -#else -#define FFI_CLOSURE_PTR(X) (X) -#define FFI_RESTORE_PTR(X) (X) -#endif - FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, @@ -392,7 +351,7 @@ ffi_prep_closure_loc (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, - void *codeloc); + void*codeloc); #ifdef __sgi # pragma pack 8 @@ -410,7 +369,7 @@ typedef struct { /* If this is enabled, then a raw closure has the same layout as a regular closure. We use this to install an intermediate - handler to do the translation, void** -> ffi_raw*. */ + handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void *this_closure; @@ -466,14 +425,14 @@ FFI_API ffi_status ffi_prep_java_raw_closure (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), - void *user_data) __attribute__((deprecated)); + void *user_data); FFI_API ffi_status ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data, - void *codeloc) __attribute__((deprecated)); + void *codeloc); #endif #endif /* FFI_CLOSURES */ @@ -528,6 +487,40 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #endif +/* If these change, update src/mips/ffitarget.h. */ +#define FFI_TYPE_VOID 0 +#define FFI_TYPE_INT 1 +#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_DOUBLE 3 +#if 0 +#define FFI_TYPE_LONGDOUBLE 4 +#else +#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE +#endif +#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_SINT8 6 +#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_SINT16 8 +#define FFI_TYPE_UINT32 9 +#define FFI_TYPE_SINT32 10 +#define FFI_TYPE_UINT64 11 +#define FFI_TYPE_SINT64 12 +#define FFI_TYPE_STRUCT 13 +#define FFI_TYPE_POINTER 14 +#define FFI_TYPE_COMPLEX 15 +#if 1 +#define FFI_TYPE_EXT_VECTOR 16 +#else +#define FFI_TYPE_EXT_VECTOR FFI_TYPE_STRUCT +#endif + +/* This should always refer to the last type code (for sanity checks). */ +#if 1 +#define FFI_TYPE_LAST FFI_TYPE_EXT_VECTOR +#else +#define FFI_TYPE_LAST FFI_TYPE_COMPLEX +#endif + #ifdef __cplusplus } #endif @@ -536,9 +529,8 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #elif defined(__x86_64__) /* -----------------------------------------------------------------*-C-*- - libffi 3.4.4 - - Copyright (c) 2011, 2014, 2019, 2021, 2022 Anthony Green - - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. + libffi 3.3-rc0 - Copyright (c) 2011, 2014 Anthony Green + - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -593,31 +585,6 @@ extern "C" { /* ---- System configuration information --------------------------------- */ -/* If these change, update src/mips/ffitarget.h. */ -#define FFI_TYPE_VOID 0 -#define FFI_TYPE_INT 1 -#define FFI_TYPE_FLOAT 2 -#define FFI_TYPE_DOUBLE 3 -#if 1 -#define FFI_TYPE_LONGDOUBLE 4 -#else -#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE -#endif -#define FFI_TYPE_UINT8 5 -#define FFI_TYPE_SINT8 6 -#define FFI_TYPE_UINT16 7 -#define FFI_TYPE_SINT16 8 -#define FFI_TYPE_UINT32 9 -#define FFI_TYPE_SINT32 10 -#define FFI_TYPE_UINT64 11 -#define FFI_TYPE_SINT64 12 -#define FFI_TYPE_STRUCT 13 -#define FFI_TYPE_POINTER 14 -#define FFI_TYPE_COMPLEX 15 - -/* This should always refer to the last type code (for sanity checks). */ -#define FFI_TYPE_LAST FFI_TYPE_COMPLEX - #include #ifndef LIBFFI_ASM @@ -780,8 +747,7 @@ FFI_EXTERN ffi_type ffi_type_complex_longdouble; typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, - FFI_BAD_ABI, - FFI_BAD_ARGTYPE + FFI_BAD_ABI } ffi_status; typedef struct { @@ -852,15 +818,15 @@ FFI_API void ffi_java_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, - ffi_java_raw *avalue) __attribute__((deprecated)); + ffi_java_raw *avalue); #endif FFI_API -void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated)); +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw); FFI_API -void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __attribute__((deprecated)); +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args); FFI_API -size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); +size_t ffi_java_raw_size (ffi_cif *cif); /* ---- Definitions for closures ----------------------------------------- */ @@ -874,17 +840,11 @@ typedef struct { void *trampoline_table; void *trampoline_table_entry; #else - union { - char tramp[FFI_TRAMPOLINE_SIZE]; - void *ftramp; - }; + char tramp[FFI_TRAMPOLINE_SIZE]; #endif ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); void *user_data; -#if defined(_MSC_VER) && defined(_M_IX86) - void *padding; -#endif } ffi_closure #ifdef __GNUC__ __attribute__((aligned (8))) @@ -900,14 +860,6 @@ typedef struct { FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); -#if defined(PA_LINUX) || defined(PA_HPUX) -#define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) -#define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) -#else -#define FFI_CLOSURE_PTR(X) (X) -#define FFI_RESTORE_PTR(X) (X) -#endif - FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, @@ -925,7 +877,7 @@ ffi_prep_closure_loc (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, - void *codeloc); + void*codeloc); #ifdef __sgi # pragma pack 8 @@ -943,7 +895,7 @@ typedef struct { /* If this is enabled, then a raw closure has the same layout as a regular closure. We use this to install an intermediate - handler to do the translation, void** -> ffi_raw*. */ + handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void *this_closure; @@ -999,14 +951,14 @@ FFI_API ffi_status ffi_prep_java_raw_closure (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), - void *user_data) __attribute__((deprecated)); + void *user_data); FFI_API ffi_status ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data, - void *codeloc) __attribute__((deprecated)); + void *codeloc); #endif #endif /* FFI_CLOSURES */ @@ -1061,13 +1013,47 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #endif +/* If these change, update src/mips/ffitarget.h. */ +#define FFI_TYPE_VOID 0 +#define FFI_TYPE_INT 1 +#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_DOUBLE 3 +#if 1 +#define FFI_TYPE_LONGDOUBLE 4 +#else +#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE +#endif +#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_SINT8 6 +#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_SINT16 8 +#define FFI_TYPE_UINT32 9 +#define FFI_TYPE_SINT32 10 +#define FFI_TYPE_UINT64 11 +#define FFI_TYPE_SINT64 12 +#define FFI_TYPE_STRUCT 13 +#define FFI_TYPE_POINTER 14 +#define FFI_TYPE_COMPLEX 15 +#if 1 +#define FFI_TYPE_EXT_VECTOR 16 +#else +#define FFI_TYPE_EXT_VECTOR FFI_TYPE_STRUCT +#endif + +/* This should always refer to the last type code (for sanity checks). */ +#if 1 +#define FFI_TYPE_LAST FFI_TYPE_EXT_VECTOR +#else +#define FFI_TYPE_LAST FFI_TYPE_COMPLEX +#endif + #ifdef __cplusplus } #endif #endif - #else + #else #error "Unsupported architecture" #endif - + \ No newline at end of file diff --git a/NativeScript/libffi/iphonesimulator-universal/include/ffitarget.h b/NativeScript/libffi/iphonesimulator-universal/include/ffitarget.h index da9837ff..ec17373b 100644 --- a/NativeScript/libffi/iphonesimulator-universal/include/ffitarget.h +++ b/NativeScript/libffi/iphonesimulator-universal/include/ffitarget.h @@ -36,7 +36,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define FFI_SIZEOF_JAVA_RAW 4 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; -#elif defined(_WIN32) +#elif defined(_M_ARM64) #define FFI_SIZEOF_ARG 8 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; @@ -49,13 +49,8 @@ typedef enum ffi_abi { FFI_FIRST_ABI = 0, FFI_SYSV, - FFI_WIN64, FFI_LAST_ABI, -#if defined(_WIN32) - FFI_DEFAULT_ABI = FFI_WIN64 -#else FFI_DEFAULT_ABI = FFI_SYSV -#endif } ffi_abi; #endif @@ -78,22 +73,22 @@ typedef enum ffi_abi #define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE #endif -#ifdef _WIN32 +#ifdef _M_ARM64 #define FFI_EXTRA_CIF_FIELDS unsigned is_variadic #endif -#define FFI_TARGET_SPECIFIC_VARIADIC /* ---- Internal ---- */ #if defined (__APPLE__) +#define FFI_TARGET_SPECIFIC_VARIADIC #define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs -#elif !defined(_WIN32) +#elif !defined(_M_ARM64) /* iOS and Windows reserve x18 for the system. Disable Go closures until a new static chain is chosen. */ #define FFI_GO_CLOSURES 1 #endif -#ifndef _WIN32 +#ifndef _M_ARM64 /* No complex type on Windows */ #define FFI_TARGET_HAS_COMPLEX_TYPE #endif @@ -144,9 +139,6 @@ typedef enum ffi_abi #if defined (X86_64) && defined (__i386__) #undef X86_64 -#warning ****************************************************** -#warning ********** X86 IS DEFINED **************************** -#warning ****************************************************** #define X86 #endif @@ -191,9 +183,9 @@ typedef enum ffi_abi { FFI_LAST_ABI, #ifdef __GNUC__ FFI_DEFAULT_ABI = FFI_GNUW64 -#else +#else FFI_DEFAULT_ABI = FFI_WIN64 -#endif +#endif #elif defined(X86_64) || (defined (__x86_64__) && defined (X86_DARWIN)) FFI_FIRST_ABI = 1, @@ -242,31 +234,17 @@ typedef enum ffi_abi { #if defined (X86_64) || defined(X86_WIN64) \ || (defined (__x86_64__) && defined (X86_DARWIN)) -/* 4 bytes of ENDBR64 + 7 bytes of LEA + 6 bytes of JMP + 7 bytes of NOP - + 8 bytes of pointer. */ -# define FFI_TRAMPOLINE_SIZE 32 +# define FFI_TRAMPOLINE_SIZE 24 # define FFI_NATIVE_RAW_API 0 #else -/* 4 bytes of ENDBR32 + 5 bytes of MOV + 5 bytes of JMP + 2 unused - bytes. */ -# define FFI_TRAMPOLINE_SIZE 16 +# define FFI_TRAMPOLINE_SIZE 12 # define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ #endif -#if !defined(GENERATE_LIBFFI_MAP) && defined(__CET__) -# include -# if (__CET__ & 1) != 0 -# define ENDBR_PRESENT -# endif -# define _CET_NOTRACK notrack -#else -# define _CET_ENDBR -# define _CET_NOTRACK #endif -#endif #else #error "Unsupported architecture" #endif - + \ No newline at end of file diff --git a/NativeScript/libffi/iphonesimulator-universal/libffi.a b/NativeScript/libffi/iphonesimulator-universal/libffi.a index f2ae497a..3a56d60d 100644 Binary files a/NativeScript/libffi/iphonesimulator-universal/libffi.a and b/NativeScript/libffi/iphonesimulator-universal/libffi.a differ diff --git a/NativeScript/libffi/macosx-universal/include/ffi.h b/NativeScript/libffi/macosx-universal/include/ffi.h index 017cf194..673a50d8 100644 --- a/NativeScript/libffi/macosx-universal/include/ffi.h +++ b/NativeScript/libffi/macosx-universal/include/ffi.h @@ -3,9 +3,8 @@ #if defined(__aarch64__) /* -----------------------------------------------------------------*-C-*- - libffi 3.4.4 - - Copyright (c) 2011, 2014, 2019, 2021, 2022 Anthony Green - - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. + libffi 3.3-rc0 - Copyright (c) 2011, 2014 Anthony Green + - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -60,31 +59,6 @@ extern "C" { /* ---- System configuration information --------------------------------- */ -/* If these change, update src/mips/ffitarget.h. */ -#define FFI_TYPE_VOID 0 -#define FFI_TYPE_INT 1 -#define FFI_TYPE_FLOAT 2 -#define FFI_TYPE_DOUBLE 3 -#if 0 -#define FFI_TYPE_LONGDOUBLE 4 -#else -#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE -#endif -#define FFI_TYPE_UINT8 5 -#define FFI_TYPE_SINT8 6 -#define FFI_TYPE_UINT16 7 -#define FFI_TYPE_SINT16 8 -#define FFI_TYPE_UINT32 9 -#define FFI_TYPE_SINT32 10 -#define FFI_TYPE_UINT64 11 -#define FFI_TYPE_SINT64 12 -#define FFI_TYPE_STRUCT 13 -#define FFI_TYPE_POINTER 14 -#define FFI_TYPE_COMPLEX 15 - -/* This should always refer to the last type code (for sanity checks). */ -#define FFI_TYPE_LAST FFI_TYPE_COMPLEX - #include #ifndef LIBFFI_ASM @@ -247,8 +221,7 @@ FFI_EXTERN ffi_type ffi_type_complex_longdouble; typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, - FFI_BAD_ABI, - FFI_BAD_ARGTYPE + FFI_BAD_ABI } ffi_status; typedef struct { @@ -319,15 +292,15 @@ FFI_API void ffi_java_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, - ffi_java_raw *avalue) __attribute__((deprecated)); + ffi_java_raw *avalue); #endif FFI_API -void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated)); +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw); FFI_API -void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __attribute__((deprecated)); +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args); FFI_API -size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); +size_t ffi_java_raw_size (ffi_cif *cif); /* ---- Definitions for closures ----------------------------------------- */ @@ -341,17 +314,11 @@ typedef struct { void *trampoline_table; void *trampoline_table_entry; #else - union { - char tramp[FFI_TRAMPOLINE_SIZE]; - void *ftramp; - }; + char tramp[FFI_TRAMPOLINE_SIZE]; #endif ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); void *user_data; -#if defined(_MSC_VER) && defined(_M_IX86) - void *padding; -#endif } ffi_closure #ifdef __GNUC__ __attribute__((aligned (8))) @@ -367,14 +334,6 @@ typedef struct { FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); -#if defined(PA_LINUX) || defined(PA_HPUX) -#define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) -#define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) -#else -#define FFI_CLOSURE_PTR(X) (X) -#define FFI_RESTORE_PTR(X) (X) -#endif - FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, @@ -392,7 +351,7 @@ ffi_prep_closure_loc (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, - void *codeloc); + void*codeloc); #ifdef __sgi # pragma pack 8 @@ -410,7 +369,7 @@ typedef struct { /* If this is enabled, then a raw closure has the same layout as a regular closure. We use this to install an intermediate - handler to do the translation, void** -> ffi_raw*. */ + handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void *this_closure; @@ -466,14 +425,14 @@ FFI_API ffi_status ffi_prep_java_raw_closure (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), - void *user_data) __attribute__((deprecated)); + void *user_data); FFI_API ffi_status ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data, - void *codeloc) __attribute__((deprecated)); + void *codeloc); #endif #endif /* FFI_CLOSURES */ @@ -528,6 +487,40 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #endif +/* If these change, update src/mips/ffitarget.h. */ +#define FFI_TYPE_VOID 0 +#define FFI_TYPE_INT 1 +#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_DOUBLE 3 +#if 0 +#define FFI_TYPE_LONGDOUBLE 4 +#else +#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE +#endif +#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_SINT8 6 +#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_SINT16 8 +#define FFI_TYPE_UINT32 9 +#define FFI_TYPE_SINT32 10 +#define FFI_TYPE_UINT64 11 +#define FFI_TYPE_SINT64 12 +#define FFI_TYPE_STRUCT 13 +#define FFI_TYPE_POINTER 14 +#define FFI_TYPE_COMPLEX 15 +#if 1 +#define FFI_TYPE_EXT_VECTOR 16 +#else +#define FFI_TYPE_EXT_VECTOR FFI_TYPE_STRUCT +#endif + +/* This should always refer to the last type code (for sanity checks). */ +#if 1 +#define FFI_TYPE_LAST FFI_TYPE_EXT_VECTOR +#else +#define FFI_TYPE_LAST FFI_TYPE_COMPLEX +#endif + #ifdef __cplusplus } #endif @@ -536,9 +529,8 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #elif defined(__x86_64__) /* -----------------------------------------------------------------*-C-*- - libffi 3.4.4 - - Copyright (c) 2011, 2014, 2019, 2021, 2022 Anthony Green - - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. + libffi 3.3-rc0 - Copyright (c) 2011, 2014 Anthony Green + - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -593,31 +585,6 @@ extern "C" { /* ---- System configuration information --------------------------------- */ -/* If these change, update src/mips/ffitarget.h. */ -#define FFI_TYPE_VOID 0 -#define FFI_TYPE_INT 1 -#define FFI_TYPE_FLOAT 2 -#define FFI_TYPE_DOUBLE 3 -#if 1 -#define FFI_TYPE_LONGDOUBLE 4 -#else -#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE -#endif -#define FFI_TYPE_UINT8 5 -#define FFI_TYPE_SINT8 6 -#define FFI_TYPE_UINT16 7 -#define FFI_TYPE_SINT16 8 -#define FFI_TYPE_UINT32 9 -#define FFI_TYPE_SINT32 10 -#define FFI_TYPE_UINT64 11 -#define FFI_TYPE_SINT64 12 -#define FFI_TYPE_STRUCT 13 -#define FFI_TYPE_POINTER 14 -#define FFI_TYPE_COMPLEX 15 - -/* This should always refer to the last type code (for sanity checks). */ -#define FFI_TYPE_LAST FFI_TYPE_COMPLEX - #include #ifndef LIBFFI_ASM @@ -780,8 +747,7 @@ FFI_EXTERN ffi_type ffi_type_complex_longdouble; typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, - FFI_BAD_ABI, - FFI_BAD_ARGTYPE + FFI_BAD_ABI } ffi_status; typedef struct { @@ -852,15 +818,15 @@ FFI_API void ffi_java_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, - ffi_java_raw *avalue) __attribute__((deprecated)); + ffi_java_raw *avalue); #endif FFI_API -void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated)); +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw); FFI_API -void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __attribute__((deprecated)); +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args); FFI_API -size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); +size_t ffi_java_raw_size (ffi_cif *cif); /* ---- Definitions for closures ----------------------------------------- */ @@ -874,17 +840,11 @@ typedef struct { void *trampoline_table; void *trampoline_table_entry; #else - union { - char tramp[FFI_TRAMPOLINE_SIZE]; - void *ftramp; - }; + char tramp[FFI_TRAMPOLINE_SIZE]; #endif ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); void *user_data; -#if defined(_MSC_VER) && defined(_M_IX86) - void *padding; -#endif } ffi_closure #ifdef __GNUC__ __attribute__((aligned (8))) @@ -900,14 +860,6 @@ typedef struct { FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); -#if defined(PA_LINUX) || defined(PA_HPUX) -#define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) -#define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) -#else -#define FFI_CLOSURE_PTR(X) (X) -#define FFI_RESTORE_PTR(X) (X) -#endif - FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, @@ -925,7 +877,7 @@ ffi_prep_closure_loc (ffi_closure*, ffi_cif *, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, - void *codeloc); + void*codeloc); #ifdef __sgi # pragma pack 8 @@ -943,7 +895,7 @@ typedef struct { /* If this is enabled, then a raw closure has the same layout as a regular closure. We use this to install an intermediate - handler to do the translation, void** -> ffi_raw*. */ + handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void *this_closure; @@ -999,14 +951,14 @@ FFI_API ffi_status ffi_prep_java_raw_closure (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), - void *user_data) __attribute__((deprecated)); + void *user_data); FFI_API ffi_status ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data, - void *codeloc) __attribute__((deprecated)); + void *codeloc); #endif #endif /* FFI_CLOSURES */ @@ -1061,13 +1013,47 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #endif +/* If these change, update src/mips/ffitarget.h. */ +#define FFI_TYPE_VOID 0 +#define FFI_TYPE_INT 1 +#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_DOUBLE 3 +#if 1 +#define FFI_TYPE_LONGDOUBLE 4 +#else +#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE +#endif +#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_SINT8 6 +#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_SINT16 8 +#define FFI_TYPE_UINT32 9 +#define FFI_TYPE_SINT32 10 +#define FFI_TYPE_UINT64 11 +#define FFI_TYPE_SINT64 12 +#define FFI_TYPE_STRUCT 13 +#define FFI_TYPE_POINTER 14 +#define FFI_TYPE_COMPLEX 15 +#if 1 +#define FFI_TYPE_EXT_VECTOR 16 +#else +#define FFI_TYPE_EXT_VECTOR FFI_TYPE_STRUCT +#endif + +/* This should always refer to the last type code (for sanity checks). */ +#if 1 +#define FFI_TYPE_LAST FFI_TYPE_EXT_VECTOR +#else +#define FFI_TYPE_LAST FFI_TYPE_COMPLEX +#endif + #ifdef __cplusplus } #endif #endif - #else + #else #error "Unsupported architecture" #endif - + \ No newline at end of file diff --git a/NativeScript/libffi/macosx-universal/include/ffitarget.h b/NativeScript/libffi/macosx-universal/include/ffitarget.h index de3ea9f2..0901fabd 100644 --- a/NativeScript/libffi/macosx-universal/include/ffitarget.h +++ b/NativeScript/libffi/macosx-universal/include/ffitarget.h @@ -36,7 +36,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define FFI_SIZEOF_JAVA_RAW 4 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; -#elif defined(_WIN32) +#elif defined(_M_ARM64) #define FFI_SIZEOF_ARG 8 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; @@ -49,13 +49,8 @@ typedef enum ffi_abi { FFI_FIRST_ABI = 0, FFI_SYSV, - FFI_WIN64, FFI_LAST_ABI, -#if defined(_WIN32) - FFI_DEFAULT_ABI = FFI_WIN64 -#else FFI_DEFAULT_ABI = FFI_SYSV -#endif } ffi_abi; #endif @@ -78,22 +73,22 @@ typedef enum ffi_abi #define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE #endif -#ifdef _WIN32 +#ifdef _M_ARM64 #define FFI_EXTRA_CIF_FIELDS unsigned is_variadic #endif -#define FFI_TARGET_SPECIFIC_VARIADIC /* ---- Internal ---- */ #if defined (__APPLE__) +#define FFI_TARGET_SPECIFIC_VARIADIC #define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs -#elif !defined(_WIN32) +#elif !defined(_M_ARM64) /* iOS and Windows reserve x18 for the system. Disable Go closures until a new static chain is chosen. */ #define FFI_GO_CLOSURES 1 #endif -#ifndef _WIN32 +#ifndef _M_ARM64 /* No complex type on Windows */ #define FFI_TARGET_HAS_COMPLEX_TYPE #endif @@ -144,9 +139,6 @@ typedef enum ffi_abi #if defined (X86_64) && defined (__i386__) #undef X86_64 -#warning ****************************************************** -#warning ********** X86 IS DEFINED **************************** -#warning ****************************************************** #define X86 #endif @@ -191,9 +183,9 @@ typedef enum ffi_abi { FFI_LAST_ABI, #ifdef __GNUC__ FFI_DEFAULT_ABI = FFI_GNUW64 -#else +#else FFI_DEFAULT_ABI = FFI_WIN64 -#endif +#endif #elif defined(X86_64) || (defined (__x86_64__) && defined (X86_DARWIN)) FFI_FIRST_ABI = 1, @@ -242,31 +234,17 @@ typedef enum ffi_abi { #if defined (X86_64) || defined(X86_WIN64) \ || (defined (__x86_64__) && defined (X86_DARWIN)) -/* 4 bytes of ENDBR64 + 7 bytes of LEA + 6 bytes of JMP + 7 bytes of NOP - + 8 bytes of pointer. */ -# define FFI_TRAMPOLINE_SIZE 32 +# define FFI_TRAMPOLINE_SIZE 24 # define FFI_NATIVE_RAW_API 0 #else -/* 4 bytes of ENDBR32 + 5 bytes of MOV + 5 bytes of JMP + 2 unused - bytes. */ -# define FFI_TRAMPOLINE_SIZE 16 +# define FFI_TRAMPOLINE_SIZE 12 # define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ #endif -#if !defined(GENERATE_LIBFFI_MAP) && defined(__CET__) -# include -# if (__CET__ & 1) != 0 -# define ENDBR_PRESENT -# endif -# define _CET_NOTRACK notrack -#else -# define _CET_ENDBR -# define _CET_NOTRACK #endif -#endif #else #error "Unsupported architecture" #endif - + \ No newline at end of file diff --git a/NativeScript/libffi/macosx-universal/libffi.a b/NativeScript/libffi/macosx-universal/libffi.a index 1d933687..a5c56a1a 100644 Binary files a/NativeScript/libffi/macosx-universal/libffi.a and b/NativeScript/libffi/macosx-universal/libffi.a differ diff --git a/NativeScript/napi/common/jsr_common.h b/NativeScript/napi/common/jsr_common.h index 76dbb466..ed6b7a4d 100644 --- a/NativeScript/napi/common/jsr_common.h +++ b/NativeScript/napi/common/jsr_common.h @@ -28,4 +28,8 @@ napi_status js_run_cached_script(napi_env env, const char * file, napi_value scr napi_status js_get_runtime_version(napi_env env, napi_value* version); +// Invoked by engine-specific env teardown to execute registered node-api +// cleanup hooks for the environment before it is released. +void js_run_env_cleanup_hooks(napi_env env); + #endif //TEST_APP_JSR_COMMON_H diff --git a/NativeScript/napi/common/native_api_util.h b/NativeScript/napi/common/native_api_util.h index 2986b541..2a1676ea 100644 --- a/NativeScript/napi/common/native_api_util.h +++ b/NativeScript/napi/common/native_api_util.h @@ -79,7 +79,7 @@ struct char_traits { #define NAPI_CALLBACK_BEGIN_VARGS() \ napi_status status; \ - size_t argc; \ + size_t argc = 0; \ void* data; \ napi_value jsThis; \ NAPI_GUARD(napi_get_cb_info(env, info, &argc, nullptr, &jsThis, &data)) { \ @@ -609,4 +609,4 @@ inline void napi_inherits(napi_env env, napi_value ctor, } // namespace napi_util -#endif /* NATIVE_API_UTIL_H_ */ \ No newline at end of file +#endif /* NATIVE_API_UTIL_H_ */ diff --git a/NativeScript/napi/jsc/jsr.cpp b/NativeScript/napi/jsc/jsr.cpp index d5b66d58..aa265fe9 100644 --- a/NativeScript/napi/jsc/jsr.cpp +++ b/NativeScript/napi/jsc/jsr.cpp @@ -42,6 +42,7 @@ napi_status js_unlock_env(napi_env env) { napi_status js_free_napi_env(napi_env env) { if (env == nullptr) return napi_invalid_arg; + js_run_env_cleanup_hooks(env); delete env; return napi_ok; } diff --git a/NativeScript/napi/quickjs/jsr.cpp b/NativeScript/napi/quickjs/jsr.cpp index b9ec43e7..19cabf08 100644 --- a/NativeScript/napi/quickjs/jsr.cpp +++ b/NativeScript/napi/quickjs/jsr.cpp @@ -34,6 +34,7 @@ napi_status js_free_napi_env(napi_env env) { JSR* jsr = JSR::env_to_jsr_cache.Get(env); delete jsr; JSR::env_to_jsr_cache.Remove(env); + js_run_env_cleanup_hooks(env); return qjs_free_napi_env(env); } diff --git a/NativeScript/napi/quickjs/quickjs-api.c b/NativeScript/napi/quickjs/quickjs-api.c index d2a3276b..4b1146ce 100644 --- a/NativeScript/napi/quickjs/quickjs-api.c +++ b/NativeScript/napi/quickjs/quickjs-api.c @@ -4101,7 +4101,7 @@ JSEngineCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst * napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { assert(env && runtime); - *env = (napi_env__ *) mi_malloc(sizeof(struct napi_env__)); + *env = (napi_env__ *) mi_zalloc(sizeof(struct napi_env__)); (*env)->runtime = runtime; @@ -4164,6 +4164,7 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { (*env)->isThrowNull = false; (*env)->gcBefore = NULL; (*env)->gcAfter = NULL; + (*env)->referenceSymbolValue = JS_UNDEFINED; LIST_INIT(&(*env)->handleScopeList); @@ -4215,36 +4216,6 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) { napi_status qjs_free_napi_env(napi_env env) { CHECK_ARG(env) - // Free all handle scopes - napi_handle_scope handleScope, tempHandleScope; - LIST_FOREACH_SAFE(handleScope, &env->handleScopeList, node, tempHandleScope) { - struct Handle *handle, *tempHandle; - SLIST_FOREACH_SAFE(handle, &handleScope->handleList, node, tempHandle) { - JS_FreeValue(env->context, handle->value); - if (handle->type == HANDLE_HEAP_ALLOCATED) { - mi_free(handle); - } - } - LIST_REMOVE(handleScope, node); - if (handleScope->type == HANDLE_HEAP_ALLOCATED) { - mi_free(handleScope); - } - } - - // Free all references - napi_ref ref, temp; - LIST_FOREACH_SAFE(ref, &env->referencesList, node, temp) { - LIST_REMOVE(ref, node); - JS_FreeValue(env->context, ref->value); - mi_free(ref); - } - - // Free Reference Symbol - JS_FreeValue(env->context, env->referenceSymbolValue); - - // Free Finalization Registry - JS_FreeValue(env->context, env->finalizationRegistry); - // Free Instance Data if (env->instanceData && env->instanceData->finalizeCallback) { env->instanceData->finalizeCallback(env, env->instanceData->data, @@ -4260,26 +4231,6 @@ napi_status qjs_free_napi_env(napi_env env) { mi_free(env->gcBefore); } - // Free Atoms - JS_FreeAtom(env->context, env->atoms.napi_external); - JS_FreeAtom(env->context, env->atoms.registerFinalizer); - JS_FreeAtom(env->context, env->atoms.buffer); - JS_FreeAtom(env->context, env->atoms.napi_buffer); - JS_FreeAtom(env->context, env->atoms.byteLength); - JS_FreeAtom(env->context, env->atoms.byteOffset); - JS_FreeAtom(env->context, env->atoms.constructor); - JS_FreeAtom(env->context, env->atoms.prototype); - JS_FreeAtom(env->context, env->atoms.name); - JS_FreeAtom(env->context, env->atoms.length); - JS_FreeAtom(env->context, env->atoms.is); - JS_FreeAtom(env->context, env->atoms.freeze); - JS_FreeAtom(env->context, env->atoms.seal); - JS_FreeAtom(env->context, env->atoms.Symbol); - JS_FreeAtom(env->context, env->atoms.NAPISymbolFor); - JS_FreeAtom(env->context, env->atoms.object); - JS_FreeAtom(env->context, env->atoms.napi_typetag); - JS_FreeAtom(env->context, env->atoms.weakref); - // Free Context JS_FreeContext(env->context); diff --git a/NativeScript/napi/v8/include/APIDesign.md b/NativeScript/napi/v8/include/APIDesign.md deleted file mode 100644 index fe42c8ed..00000000 --- a/NativeScript/napi/v8/include/APIDesign.md +++ /dev/null @@ -1,72 +0,0 @@ -# The V8 public C++ API - -# Overview - -The V8 public C++ API aims to support four use cases: - -1. Enable applications that embed V8 (called the embedder) to configure and run - one or more instances of V8. -2. Expose ECMAScript-like capabilities to the embedder. -3. Enable the embedder to interact with ECMAScript by exposing API objects. -4. Provide access to the V8 debugger (inspector). - -# Configuring and running an instance of V8 - -V8 requires access to certain OS-level primitives such as the ability to -schedule work on threads, or allocate memory. - -The embedder can define how to access those primitives via the v8::Platform -interface. While V8 bundles a basic implementation, embedders are highly -encouraged to implement v8::Platform themselves. - -Currently, the v8::ArrayBuffer::Allocator is passed to the v8::Isolate factory -method, however, conceptually it should also be part of the v8::Platform since -all instances of V8 should share one allocator. - -Once the v8::Platform is configured, an v8::Isolate can be created. All -further interactions with V8 should explicitly reference the v8::Isolate they -refer to. All API methods should eventually take an v8::Isolate parameter. - -When a given instance of V8 is no longer needed, it can be destroyed by -disposing the respective v8::Isolate. If the embedder wishes to free all memory -associated with the v8::Isolate, it has to first clear all global handles -associated with that v8::Isolate. - -# ECMAScript-like capabilities - -In general, the C++ API shouldn't enable capabilities that aren't available to -scripts running in V8. Experience has shown that it's not possible to maintain -such API methods in the long term. However, capabilities also available to -scripts, i.e., ones that are defined in the ECMAScript standard are there to -stay, and we can safely expose them to embedders. - -The C++ API should also be pleasant to use, and not require learning new -paradigms. Similarly to how the API exposed to scripts aims to provide good -ergonomics, we should aim to provide a reasonable developer experience for this -API surface. - -ECMAScript makes heavy use of exceptions, however, V8's C++ code doesn't use -C++ exceptions. Therefore, all API methods that can throw exceptions should -indicate so by returning a v8::Maybe<> or v8::MaybeLocal<> result, -and by taking a v8::Local<v8::Context> parameter that indicates in which -context a possible exception should be thrown. - -# API objects - -V8 allows embedders to define special objects that expose additional -capabilities and APIs to scripts. The most prominent example is exposing the -HTML DOM in Blink. Other examples are e.g. node.js. It is less clear what kind -of capabilities we want to expose via this API surface. As a rule of thumb, we -want to expose operations as defined in the WebIDL and HTML spec: we -assume that those requirements are somewhat stable, and that they are a -superset of the requirements of other embedders including node.js. - -Ideally, the API surfaces defined in those specs hook into the ECMAScript spec -which in turn guarantees long-term stability of the API. - -# The V8 inspector - -All debugging capabilities of V8 should be exposed via the inspector protocol. -The exception to this are profiling features exposed via v8-profiler.h. -Changes to the inspector protocol need to ensure backwards compatibility and -commitment to maintain. diff --git a/NativeScript/napi/v8/include/DEPS b/NativeScript/napi/v8/include/DEPS deleted file mode 100644 index 21ce3d96..00000000 --- a/NativeScript/napi/v8/include/DEPS +++ /dev/null @@ -1,10 +0,0 @@ -include_rules = [ - # v8-inspector-protocol.h depends on generated files under include/inspector. - "+inspector", - "+cppgc/common.h", - # Used by v8-cppgc.h to bridge to cppgc. - "+cppgc/custom-space.h", - "+cppgc/heap-statistics.h", - "+cppgc/internal/write-barrier.h", - "+cppgc/visitor.h", -] diff --git a/NativeScript/napi/v8/include/DIR_METADATA b/NativeScript/napi/v8/include/DIR_METADATA deleted file mode 100644 index a27ea1b5..00000000 --- a/NativeScript/napi/v8/include/DIR_METADATA +++ /dev/null @@ -1,11 +0,0 @@ -# Metadata information for this directory. -# -# For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md -# -# For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto - -monorail { - component: "Blink>JavaScript>API" -} \ No newline at end of file diff --git a/NativeScript/napi/v8/include/OWNERS b/NativeScript/napi/v8/include/OWNERS deleted file mode 100644 index 535040c5..00000000 --- a/NativeScript/napi/v8/include/OWNERS +++ /dev/null @@ -1,23 +0,0 @@ -adamk@chromium.org -cbruni@chromium.org -leszeks@chromium.org -mlippautz@chromium.org -verwaest@chromium.org -yangguo@chromium.org - -per-file *DEPS=file:../COMMON_OWNERS -per-file v8-internal.h=file:../COMMON_OWNERS - -per-file v8-debug.h=file:../src/debug/OWNERS - -per-file js_protocol.pdl=file:../src/inspector/OWNERS -per-file v8-inspector*=file:../src/inspector/OWNERS -per-file v8-inspector*=file:../src/inspector/OWNERS - -# Needed by the auto_tag builder -per-file v8-version.h=v8-ci-autoroll-builder@chops-service-accounts.iam.gserviceaccount.com - -# For branch updates: -per-file v8-version.h=file:../INFRA_OWNERS -per-file v8-version.h=hablich@chromium.org -per-file v8-version.h=vahl@chromium.org diff --git a/NativeScript/napi/v8/include/cppgc/DEPS b/NativeScript/napi/v8/include/cppgc/DEPS deleted file mode 100644 index 2ec7ebbd..00000000 --- a/NativeScript/napi/v8/include/cppgc/DEPS +++ /dev/null @@ -1,9 +0,0 @@ -include_rules = [ - "-include", - "+v8config.h", - "+v8-platform.h", - "+v8-source-location.h", - "+cppgc", - "-src", - "+libplatform/libplatform.h", -] diff --git a/NativeScript/napi/v8/include/cppgc/OWNERS b/NativeScript/napi/v8/include/cppgc/OWNERS deleted file mode 100644 index 6ccabf60..00000000 --- a/NativeScript/napi/v8/include/cppgc/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -bikineev@chromium.org -omerkatz@chromium.org \ No newline at end of file diff --git a/NativeScript/napi/v8/include/cppgc/README.md b/NativeScript/napi/v8/include/cppgc/README.md deleted file mode 100644 index d825ea5b..00000000 --- a/NativeScript/napi/v8/include/cppgc/README.md +++ /dev/null @@ -1,135 +0,0 @@ -# Oilpan: C++ Garbage Collection - -Oilpan is an open-source garbage collection library for C++ that can be used stand-alone or in collaboration with V8's JavaScript garbage collector. -Oilpan implements mark-and-sweep garbage collection (GC) with limited compaction (for a subset of objects). - -**Key properties** - -- Trace-based garbage collection; -- Incremental and concurrent marking; -- Incremental and concurrent sweeping; -- Precise on-heap memory layout; -- Conservative on-stack memory layout; -- Allows for collection with and without considering stack; -- Non-incremental and non-concurrent compaction for selected spaces; - -See the [Hello World](https://chromium.googlesource.com/v8/v8/+/main/samples/cppgc/hello-world.cc) example on how to get started using Oilpan to manage C++ code. - -Oilpan follows V8's project organization, see e.g. on how we accept [contributions](https://v8.dev/docs/contribute) and [provide a stable API](https://v8.dev/docs/api). - -## Threading model - -Oilpan features thread-local garbage collection and assumes heaps are not shared among threads. -In other words, objects are accessed and ultimately reclaimed by the garbage collector on the same thread that allocates them. -This allows Oilpan to run garbage collection in parallel with mutators running in other threads. - -References to objects belonging to another thread's heap are modeled using cross-thread roots. -This is even true for on-heap to on-heap references. - -Oilpan heaps may generally not be accessed from different threads unless otherwise noted. - -## Heap partitioning - -Oilpan's heaps are partitioned into spaces. -The space for an object is chosen depending on a number of criteria, e.g.: - -- Objects over 64KiB are allocated in a large object space -- Objects can be assigned to a dedicated custom space. - Custom spaces can also be marked as compactable. -- Other objects are allocated in one of the normal page spaces bucketed depending on their size. - -## Precise and conservative garbage collection - -Oilpan supports two kinds of GCs: - -1. **Conservative GC.** -A GC is called conservative when it is executed while the regular native stack is not empty. -In this case, the native stack might contain references to objects in Oilpan's heap, which should be kept alive. -The GC scans the native stack and treats the pointers discovered via the native stack as part of the root set. -This kind of GC is considered imprecise because values on stack other than references may accidentally appear as references to on-heap object, which means these objects will be kept alive despite being in practice unreachable from the application as an actual reference. - -2. **Precise GC.** -A precise GC is triggered at the end of an event loop, which is controlled by an embedder via a platform. -At this point, it is guaranteed that there are no on-stack references pointing to Oilpan's heap. -This means there is no risk of confusing other value types with references. -Oilpan has precise knowledge of on-heap object layouts, and so it knows exactly where pointers lie in memory. -Oilpan can just start marking from the regular root set and collect all garbage precisely. - -## Atomic, incremental and concurrent garbage collection - -Oilpan has three modes of operation: - -1. **Atomic GC.** -The entire GC cycle, including all its phases (e.g. see [Marking](#Marking-phase) and [Sweeping](#Sweeping-phase)), are executed back to back in a single pause. -This mode of operation is also known as Stop-The-World (STW) garbage collection. -It results in the most jank (due to a single long pause), but is overall the most efficient (e.g. no need for write barriers). - -2. **Incremental GC.** -Garbage collection work is split up into multiple steps which are interleaved with the mutator, i.e. user code chunked into tasks. -Each step is a small chunk of work that is executed either as dedicated tasks between mutator tasks or, as needed, during mutator tasks. -Using incremental GC introduces the need for write barriers that record changes to the object graph so that a consistent state is observed and no objects are accidentally considered dead and reclaimed. -The incremental steps are followed by a smaller atomic pause to finalize garbage collection. -The smaller pause times, due to smaller chunks of work, helps with reducing jank. - -3. **Concurrent GC.** -This is the most common type of GC. -It builds on top of incremental GC and offloads much of the garbage collection work away from the mutator thread and on to background threads. -Using concurrent GC allows the mutator thread to spend less time on GC and more on the actual mutator. - -## Marking phase - -The marking phase consists of the following steps: - -1. Mark all objects in the root set. - -2. Mark all objects transitively reachable from the root set by calling `Trace()` methods defined on each object. - -3. Clear out all weak handles to unreachable objects and run weak callbacks. - -The marking phase can be executed atomically in a stop-the-world manner, in which all 3 steps are executed one after the other. - -Alternatively, it can also be executed incrementally/concurrently. -With incremental/concurrent marking, step 1 is executed in a short pause after which the mutator regains control. -Step 2 is repeatedly executed in an interleaved manner with the mutator. -When the GC is ready to finalize, i.e. step 2 is (almost) finished, another short pause is triggered in which step 2 is finished and step 3 is performed. - -To prevent a user-after-free (UAF) issues it is required for Oilpan to know about all edges in the object graph. -This means that all pointers except on-stack pointers must be wrapped with Oilpan's handles (i.e., Persistent<>, Member<>, WeakMember<>). -Raw pointers to on-heap objects create an edge that Oilpan cannot observe and cause UAF issues -Thus, raw pointers shall not be used to reference on-heap objects (except for raw pointers on native stacks). - -## Sweeping phase - -The sweeping phase consists of the following steps: - -1. Invoke pre-finalizers. -At this point, no destructors have been invoked and no memory has been reclaimed. -Pre-finalizers are allowed to access any other on-heap objects, even those that may get destructed. - -2. Sweeping invokes destructors of the dead (unreachable) objects and reclaims memory to be reused by future allocations. - -Assumptions should not be made about the order and the timing of their execution. -There is no guarantee on the order in which the destructors are invoked. -That's why destructors must not access any other on-heap objects (which might have already been destructed). -If some destructor unavoidably needs to access other on-heap objects, it will have to be converted to a pre-finalizer. -The pre-finalizer is allowed to access other on-heap objects. - -The mutator is resumed before all destructors have ran. -For example, imagine a case where X is a client of Y, and Y holds a list of clients. -If the code relies on X's destructor removing X from the list, there is a risk that Y iterates the list and calls some method of X which may touch other on-heap objects. -This causes a use-after-free. -Care must be taken to make sure that X is explicitly removed from the list before the mutator resumes its execution in a way that doesn't rely on X's destructor (e.g. a pre-finalizer). - -Similar to marking, sweeping can be executed in either an atomic stop-the-world manner or incrementally/concurrently. -With incremental/concurrent sweeping, step 2 is interleaved with mutator. -Incremental/concurrent sweeping can be atomically finalized in case it is needed to trigger another GC cycle. -Even with concurrent sweeping, destructors are guaranteed to run on the thread the object has been allocated on to preserve C++ semantics. - -Notes: - -* Weak processing runs only when the holder object of the WeakMember outlives the pointed object. -If the holder object and the pointed object die at the same time, weak processing doesn't run. -It is wrong to write code assuming that the weak processing always runs. - -* Pre-finalizers are heavy because the thread needs to scan all pre-finalizers at each sweeping phase to determine which pre-finalizers should be invoked (the thread needs to invoke pre-finalizers of dead objects). -Adding pre-finalizers to frequently created objects should be avoided. diff --git a/NativeScript/napi/v8/include/cppgc/allocation.h b/NativeScript/napi/v8/include/cppgc/allocation.h deleted file mode 100644 index 69883fb3..00000000 --- a/NativeScript/napi/v8/include/cppgc/allocation.h +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_ALLOCATION_H_ -#define INCLUDE_CPPGC_ALLOCATION_H_ - -#include -#include -#include -#include -#include -#include - -#include "cppgc/custom-space.h" -#include "cppgc/internal/api-constants.h" -#include "cppgc/internal/gc-info.h" -#include "cppgc/type-traits.h" -#include "v8config.h" // NOLINT(build/include_directory) - -#if defined(__has_attribute) -#if __has_attribute(assume_aligned) -#define CPPGC_DEFAULT_ALIGNED \ - __attribute__((assume_aligned(api_constants::kDefaultAlignment))) -#define CPPGC_DOUBLE_WORD_ALIGNED \ - __attribute__((assume_aligned(2 * api_constants::kDefaultAlignment))) -#endif // __has_attribute(assume_aligned) -#endif // defined(__has_attribute) - -#if !defined(CPPGC_DEFAULT_ALIGNED) -#define CPPGC_DEFAULT_ALIGNED -#endif - -#if !defined(CPPGC_DOUBLE_WORD_ALIGNED) -#define CPPGC_DOUBLE_WORD_ALIGNED -#endif - -namespace cppgc { - -/** - * AllocationHandle is used to allocate garbage-collected objects. - */ -class AllocationHandle; - -namespace internal { - -// Similar to C++17 std::align_val_t; -enum class AlignVal : size_t {}; - -class V8_EXPORT MakeGarbageCollectedTraitInternal { - protected: - static inline void MarkObjectAsFullyConstructed(const void* payload) { - // See api_constants for an explanation of the constants. - std::atomic* atomic_mutable_bitfield = - reinterpret_cast*>( - const_cast(reinterpret_cast( - reinterpret_cast(payload) - - api_constants::kFullyConstructedBitFieldOffsetFromPayload))); - // It's safe to split use load+store here (instead of a read-modify-write - // operation), since it's guaranteed that this 16-bit bitfield is only - // modified by a single thread. This is cheaper in terms of code bloat (on - // ARM) and performance. - uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed); - value |= api_constants::kFullyConstructedBitMask; - atomic_mutable_bitfield->store(value, std::memory_order_release); - } - - // Dispatch based on compile-time information. - // - // Default implementation is for a custom space with >`kDefaultAlignment` byte - // alignment. - template - struct AllocationDispatcher final { - static void* Invoke(AllocationHandle& handle, size_t size) { - static_assert(std::is_base_of::value, - "Custom space must inherit from CustomSpaceBase."); - static_assert( - !CustomSpace::kSupportsCompaction, - "Custom spaces that support compaction do not support allocating " - "objects with non-default (i.e. word-sized) alignment."); - return MakeGarbageCollectedTraitInternal::Allocate( - handle, size, static_cast(alignment), - internal::GCInfoTrait::Index(), CustomSpace::kSpaceIndex); - } - }; - - // Fast path for regular allocations for the default space with - // `kDefaultAlignment` byte alignment. - template - struct AllocationDispatcher - final { - static void* Invoke(AllocationHandle& handle, size_t size) { - return MakeGarbageCollectedTraitInternal::Allocate( - handle, size, internal::GCInfoTrait::Index()); - } - }; - - // Default space with >`kDefaultAlignment` byte alignment. - template - struct AllocationDispatcher final { - static void* Invoke(AllocationHandle& handle, size_t size) { - return MakeGarbageCollectedTraitInternal::Allocate( - handle, size, static_cast(alignment), - internal::GCInfoTrait::Index()); - } - }; - - // Custom space with `kDefaultAlignment` byte alignment. - template - struct AllocationDispatcher - final { - static void* Invoke(AllocationHandle& handle, size_t size) { - static_assert(std::is_base_of::value, - "Custom space must inherit from CustomSpaceBase."); - return MakeGarbageCollectedTraitInternal::Allocate( - handle, size, internal::GCInfoTrait::Index(), - CustomSpace::kSpaceIndex); - } - }; - - private: - static void* CPPGC_DEFAULT_ALIGNED Allocate(cppgc::AllocationHandle&, size_t, - GCInfoIndex); - static void* CPPGC_DOUBLE_WORD_ALIGNED Allocate(cppgc::AllocationHandle&, - size_t, AlignVal, - GCInfoIndex); - static void* CPPGC_DEFAULT_ALIGNED Allocate(cppgc::AllocationHandle&, size_t, - GCInfoIndex, CustomSpaceIndex); - static void* CPPGC_DOUBLE_WORD_ALIGNED Allocate(cppgc::AllocationHandle&, - size_t, AlignVal, GCInfoIndex, - CustomSpaceIndex); - - friend class HeapObjectHeader; -}; - -} // namespace internal - -/** - * Base trait that provides utilities for advancers users that have custom - * allocation needs (e.g., overriding size). It's expected that users override - * MakeGarbageCollectedTrait (see below) and inherit from - * MakeGarbageCollectedTraitBase and make use of the low-level primitives - * offered to allocate and construct an object. - */ -template -class MakeGarbageCollectedTraitBase - : private internal::MakeGarbageCollectedTraitInternal { - private: - static_assert(internal::IsGarbageCollectedType::value, - "T needs to be a garbage collected object"); - static_assert(!IsGarbageCollectedWithMixinTypeV || - sizeof(T) <= - internal::api_constants::kLargeObjectSizeThreshold, - "GarbageCollectedMixin may not be a large object"); - - protected: - /** - * Allocates memory for an object of type T. - * - * \param handle AllocationHandle identifying the heap to allocate the object - * on. - * \param size The size that should be reserved for the object. - * \returns the memory to construct an object of type T on. - */ - V8_INLINE static void* Allocate(AllocationHandle& handle, size_t size) { - static_assert( - std::is_base_of::value, - "U of GarbageCollected must be a base of T. Check " - "GarbageCollected base class inheritance."); - static constexpr size_t kWantedAlignment = - alignof(T) < internal::api_constants::kDefaultAlignment - ? internal::api_constants::kDefaultAlignment - : alignof(T); - static_assert( - kWantedAlignment <= internal::api_constants::kMaxSupportedAlignment, - "Requested alignment larger than alignof(std::max_align_t) bytes. " - "Please file a bug to possibly get this restriction lifted."); - return AllocationDispatcher< - typename internal::GCInfoFolding< - T, typename T::ParentMostGarbageCollectedType>::ResultType, - typename SpaceTrait::Space, kWantedAlignment>::Invoke(handle, size); - } - - /** - * Marks an object as fully constructed, resulting in precise handling by the - * garbage collector. - * - * \param payload The base pointer the object is allocated at. - */ - V8_INLINE static void MarkObjectAsFullyConstructed(const void* payload) { - internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed( - payload); - } -}; - -/** - * Passed to MakeGarbageCollected to specify how many bytes should be appended - * to the allocated object. - * - * Example: - * \code - * class InlinedArray final : public GarbageCollected { - * public: - * explicit InlinedArray(size_t bytes) : size(bytes), byte_array(this + 1) {} - * void Trace(Visitor*) const {} - - * size_t size; - * char* byte_array; - * }; - * - * auto* inlined_array = MakeGarbageCollectedbyte_array[i]); - * } - * \endcode - */ -struct AdditionalBytes { - constexpr explicit AdditionalBytes(size_t bytes) : value(bytes) {} - const size_t value; -}; - -/** - * Default trait class that specifies how to construct an object of type T. - * Advanced users may override how an object is constructed using the utilities - * that are provided through MakeGarbageCollectedTraitBase. - * - * Any trait overriding construction must - * - allocate through `MakeGarbageCollectedTraitBase::Allocate`; - * - mark the object as fully constructed using - * `MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed`; - */ -template -class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase { - public: - template - static T* Call(AllocationHandle& handle, Args&&... args) { - void* memory = - MakeGarbageCollectedTraitBase::Allocate(handle, sizeof(T)); - T* object = ::new (memory) T(std::forward(args)...); - MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed(object); - return object; - } - - template - static T* Call(AllocationHandle& handle, AdditionalBytes additional_bytes, - Args&&... args) { - void* memory = MakeGarbageCollectedTraitBase::Allocate( - handle, sizeof(T) + additional_bytes.value); - T* object = ::new (memory) T(std::forward(args)...); - MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed(object); - return object; - } -}; - -/** - * Allows users to specify a post-construction callback for specific types. The - * callback is invoked on the instance of type T right after it has been - * constructed. This can be useful when the callback requires a - * fully-constructed object to be able to dispatch to virtual methods. - */ -template -struct PostConstructionCallbackTrait { - static void Call(T*) {} -}; - -/** - * Constructs a managed object of type T where T transitively inherits from - * GarbageCollected. - * - * \param args List of arguments with which an instance of T will be - * constructed. - * \returns an instance of type T. - */ -template -V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) { - T* object = - MakeGarbageCollectedTrait::Call(handle, std::forward(args)...); - PostConstructionCallbackTrait::Call(object); - return object; -} - -/** - * Constructs a managed object of type T where T transitively inherits from - * GarbageCollected. Created objects will have additional bytes appended to - * it. Allocated memory would suffice for `sizeof(T) + additional_bytes`. - * - * \param additional_bytes Denotes how many bytes to append to T. - * \param args List of arguments with which an instance of T will be - * constructed. - * \returns an instance of type T. - */ -template -V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, - AdditionalBytes additional_bytes, - Args&&... args) { - T* object = MakeGarbageCollectedTrait::Call(handle, additional_bytes, - std::forward(args)...); - PostConstructionCallbackTrait::Call(object); - return object; -} - -} // namespace cppgc - -#undef CPPGC_DEFAULT_ALIGNED -#undef CPPGC_DOUBLE_WORD_ALIGNED - -#endif // INCLUDE_CPPGC_ALLOCATION_H_ diff --git a/NativeScript/napi/v8/include/cppgc/common.h b/NativeScript/napi/v8/include/cppgc/common.h deleted file mode 100644 index 96103836..00000000 --- a/NativeScript/napi/v8/include/cppgc/common.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_COMMON_H_ -#define INCLUDE_CPPGC_COMMON_H_ - -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -/** - * Indicator for the stack state of the embedder. - */ -enum class EmbedderStackState { - /** - * Stack may contain interesting heap pointers. - */ - kMayContainHeapPointers, - /** - * Stack does not contain any interesting heap pointers. - */ - kNoHeapPointers, -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_COMMON_H_ diff --git a/NativeScript/napi/v8/include/cppgc/cross-thread-persistent.h b/NativeScript/napi/v8/include/cppgc/cross-thread-persistent.h deleted file mode 100644 index a5f8bac0..00000000 --- a/NativeScript/napi/v8/include/cppgc/cross-thread-persistent.h +++ /dev/null @@ -1,466 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ -#define INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ - -#include - -#include "cppgc/internal/persistent-node.h" -#include "cppgc/internal/pointer-policies.h" -#include "cppgc/persistent.h" -#include "cppgc/visitor.h" - -namespace cppgc { -namespace internal { - -// Wrapper around PersistentBase that allows accessing poisoned memory when -// using ASAN. This is needed as the GC of the heap that owns the value -// of a CTP, may clear it (heap termination, weakness) while the object -// holding the CTP may be poisoned as itself may be deemed dead. -class CrossThreadPersistentBase : public PersistentBase { - public: - CrossThreadPersistentBase() = default; - explicit CrossThreadPersistentBase(const void* raw) : PersistentBase(raw) {} - - V8_CLANG_NO_SANITIZE("address") const void* GetValueFromGC() const { - return raw_; - } - - V8_CLANG_NO_SANITIZE("address") - PersistentNode* GetNodeFromGC() const { return node_; } - - V8_CLANG_NO_SANITIZE("address") - void ClearFromGC() const { - raw_ = nullptr; - SetNodeSafe(nullptr); - } - - // GetNodeSafe() can be used for a thread-safe IsValid() check in a - // double-checked locking pattern. See ~BasicCrossThreadPersistent. - PersistentNode* GetNodeSafe() const { - return reinterpret_cast*>(&node_)->load( - std::memory_order_acquire); - } - - // The GC writes using SetNodeSafe() while holding the lock. - V8_CLANG_NO_SANITIZE("address") - void SetNodeSafe(PersistentNode* value) const { -#if defined(__has_feature) -#if __has_feature(address_sanitizer) -#define V8_IS_ASAN 1 -#endif -#endif - -#ifdef V8_IS_ASAN - __atomic_store(&node_, &value, __ATOMIC_RELEASE); -#else // !V8_IS_ASAN - // Non-ASAN builds can use atomics. This also covers MSVC which does not - // have the __atomic_store intrinsic. - reinterpret_cast*>(&node_)->store( - value, std::memory_order_release); -#endif // !V8_IS_ASAN - -#undef V8_IS_ASAN - } -}; - -template -class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, - public LocationPolicy, - private WeaknessPolicy, - private CheckingPolicy { - public: - using typename WeaknessPolicy::IsStrongPersistent; - using PointeeType = T; - - ~BasicCrossThreadPersistent() { - // This implements fast path for destroying empty/sentinel. - // - // Simplified version of `AssignUnsafe()` to allow calling without a - // complete type `T`. Uses double-checked locking with a simple thread-safe - // check for a valid handle based on a node. - if (GetNodeSafe()) { - PersistentRegionLock guard; - const void* old_value = GetValue(); - // The fast path check (GetNodeSafe()) does not acquire the lock. Recheck - // validity while holding the lock to ensure the reference has not been - // cleared. - if (IsValid(old_value)) { - CrossThreadPersistentRegion& region = - this->GetPersistentRegion(old_value); - region.FreeNode(GetNode()); - SetNode(nullptr); - } else { - CPPGC_DCHECK(!GetNode()); - } - } - // No need to call SetValue() as the handle is not used anymore. This can - // leave behind stale sentinel values but will always destroy the underlying - // node. - } - - BasicCrossThreadPersistent( - const SourceLocation& loc = SourceLocation::Current()) - : LocationPolicy(loc) {} - - BasicCrossThreadPersistent( - std::nullptr_t, const SourceLocation& loc = SourceLocation::Current()) - : LocationPolicy(loc) {} - - BasicCrossThreadPersistent( - SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) - : CrossThreadPersistentBase(s), LocationPolicy(loc) {} - - BasicCrossThreadPersistent( - T* raw, const SourceLocation& loc = SourceLocation::Current()) - : CrossThreadPersistentBase(raw), LocationPolicy(loc) { - if (!IsValid(raw)) return; - PersistentRegionLock guard; - CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); - SetNode(region.AllocateNode(this, &TraceAsRoot)); - this->CheckPointer(raw); - } - - class UnsafeCtorTag { - private: - UnsafeCtorTag() = default; - template - friend class BasicCrossThreadPersistent; - }; - - BasicCrossThreadPersistent( - UnsafeCtorTag, T* raw, - const SourceLocation& loc = SourceLocation::Current()) - : CrossThreadPersistentBase(raw), LocationPolicy(loc) { - if (!IsValid(raw)) return; - CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); - SetNode(region.AllocateNode(this, &TraceAsRoot)); - this->CheckPointer(raw); - } - - BasicCrossThreadPersistent( - T& raw, const SourceLocation& loc = SourceLocation::Current()) - : BasicCrossThreadPersistent(&raw, loc) {} - - template ::value>> - BasicCrossThreadPersistent( - internal::BasicMember - member, - const SourceLocation& loc = SourceLocation::Current()) - : BasicCrossThreadPersistent(member.Get(), loc) {} - - BasicCrossThreadPersistent( - const BasicCrossThreadPersistent& other, - const SourceLocation& loc = SourceLocation::Current()) - : BasicCrossThreadPersistent(loc) { - // Invoke operator=. - *this = other; - } - - // Heterogeneous ctor. - template ::value>> - BasicCrossThreadPersistent( - const BasicCrossThreadPersistent& other, - const SourceLocation& loc = SourceLocation::Current()) - : BasicCrossThreadPersistent(loc) { - *this = other; - } - - BasicCrossThreadPersistent( - BasicCrossThreadPersistent&& other, - const SourceLocation& loc = SourceLocation::Current()) noexcept { - // Invoke operator=. - *this = std::move(other); - } - - BasicCrossThreadPersistent& operator=( - const BasicCrossThreadPersistent& other) { - PersistentRegionLock guard; - AssignSafe(guard, other.Get()); - return *this; - } - - template ::value>> - BasicCrossThreadPersistent& operator=( - const BasicCrossThreadPersistent& other) { - PersistentRegionLock guard; - AssignSafe(guard, other.Get()); - return *this; - } - - BasicCrossThreadPersistent& operator=(BasicCrossThreadPersistent&& other) { - if (this == &other) return *this; - Clear(); - PersistentRegionLock guard; - PersistentBase::operator=(std::move(other)); - LocationPolicy::operator=(std::move(other)); - if (!IsValid(GetValue())) return *this; - GetNode()->UpdateOwner(this); - other.SetValue(nullptr); - other.SetNode(nullptr); - this->CheckPointer(Get()); - return *this; - } - - /** - * Assigns a raw pointer. - * - * Note: **Not thread-safe.** - */ - BasicCrossThreadPersistent& operator=(T* other) { - AssignUnsafe(other); - return *this; - } - - // Assignment from member. - template ::value>> - BasicCrossThreadPersistent& operator=( - internal::BasicMember - member) { - return operator=(member.Get()); - } - - /** - * Assigns a nullptr. - * - * \returns the handle. - */ - BasicCrossThreadPersistent& operator=(std::nullptr_t) { - Clear(); - return *this; - } - - /** - * Assigns the sentinel pointer. - * - * \returns the handle. - */ - BasicCrossThreadPersistent& operator=(SentinelPointer s) { - PersistentRegionLock guard; - AssignSafe(guard, s); - return *this; - } - - /** - * Returns a pointer to the stored object. - * - * Note: **Not thread-safe.** - * - * \returns a pointer to the stored object. - */ - // CFI cast exemption to allow passing SentinelPointer through T* and support - // heterogeneous assignments between different Member and Persistent handles - // based on their actual types. - V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { - return static_cast(const_cast(GetValue())); - } - - /** - * Clears the stored object. - */ - void Clear() { - PersistentRegionLock guard; - AssignSafe(guard, nullptr); - } - - /** - * Returns a pointer to the stored object and releases it. - * - * Note: **Not thread-safe.** - * - * \returns a pointer to the stored object. - */ - T* Release() { - T* result = Get(); - Clear(); - return result; - } - - /** - * Conversio to boolean. - * - * Note: **Not thread-safe.** - * - * \returns true if an actual object has been stored and false otherwise. - */ - explicit operator bool() const { return Get(); } - - /** - * Conversion to object of type T. - * - * Note: **Not thread-safe.** - * - * \returns the object. - */ - operator T*() const { return Get(); } - - /** - * Dereferences the stored object. - * - * Note: **Not thread-safe.** - */ - T* operator->() const { return Get(); } - T& operator*() const { return *Get(); } - - template - BasicCrossThreadPersistent - To() const { - using OtherBasicCrossThreadPersistent = - BasicCrossThreadPersistent; - PersistentRegionLock guard; - return OtherBasicCrossThreadPersistent( - typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(), - static_cast(Get())); - } - - template ::IsStrongPersistent::value>::type> - BasicCrossThreadPersistent - Lock() const { - return BasicCrossThreadPersistent< - U, internal::StrongCrossThreadPersistentPolicy>(*this); - } - - private: - static bool IsValid(const void* ptr) { - return ptr && ptr != kSentinelPointer; - } - - static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) { - root_visitor.Trace(*static_cast(ptr)); - } - - void AssignUnsafe(T* ptr) { - const void* old_value = GetValue(); - if (IsValid(old_value)) { - PersistentRegionLock guard; - old_value = GetValue(); - // The fast path check (IsValid()) does not acquire the lock. Reload - // the value to ensure the reference has not been cleared. - if (IsValid(old_value)) { - CrossThreadPersistentRegion& region = - this->GetPersistentRegion(old_value); - if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { - SetValue(ptr); - this->CheckPointer(ptr); - return; - } - region.FreeNode(GetNode()); - SetNode(nullptr); - } else { - CPPGC_DCHECK(!GetNode()); - } - } - SetValue(ptr); - if (!IsValid(ptr)) return; - PersistentRegionLock guard; - SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); - this->CheckPointer(ptr); - } - - void AssignSafe(PersistentRegionLock&, T* ptr) { - PersistentRegionLock::AssertLocked(); - const void* old_value = GetValue(); - if (IsValid(old_value)) { - CrossThreadPersistentRegion& region = - this->GetPersistentRegion(old_value); - if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { - SetValue(ptr); - this->CheckPointer(ptr); - return; - } - region.FreeNode(GetNode()); - SetNode(nullptr); - } - SetValue(ptr); - if (!IsValid(ptr)) return; - SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); - this->CheckPointer(ptr); - } - - void ClearFromGC() const { - if (IsValid(GetValueFromGC())) { - WeaknessPolicy::GetPersistentRegion(GetValueFromGC()) - .FreeNode(GetNodeFromGC()); - CrossThreadPersistentBase::ClearFromGC(); - } - } - - // See Get() for details. - V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") - T* GetFromGC() const { - return static_cast(const_cast(GetValueFromGC())); - } - - friend class internal::RootVisitor; -}; - -template -struct IsWeak< - BasicCrossThreadPersistent> - : std::true_type {}; - -} // namespace internal - -namespace subtle { - -/** - * **DO NOT USE: Has known caveats, see below.** - * - * CrossThreadPersistent allows retaining objects from threads other than the - * thread the owning heap is operating on. - * - * Known caveats: - * - Does not protect the heap owning an object from terminating. - * - Reaching transitively through the graph is unsupported as objects may be - * moved concurrently on the thread owning the object. - */ -template -using CrossThreadPersistent = internal::BasicCrossThreadPersistent< - T, internal::StrongCrossThreadPersistentPolicy>; - -/** - * **DO NOT USE: Has known caveats, see below.** - * - * CrossThreadPersistent allows weakly retaining objects from threads other than - * the thread the owning heap is operating on. - * - * Known caveats: - * - Does not protect the heap owning an object from terminating. - * - Reaching transitively through the graph is unsupported as objects may be - * moved concurrently on the thread owning the object. - */ -template -using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent< - T, internal::WeakCrossThreadPersistentPolicy>; - -} // namespace subtle -} // namespace cppgc - -#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ diff --git a/NativeScript/napi/v8/include/cppgc/custom-space.h b/NativeScript/napi/v8/include/cppgc/custom-space.h deleted file mode 100644 index 757c4fde..00000000 --- a/NativeScript/napi/v8/include/cppgc/custom-space.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_CUSTOM_SPACE_H_ -#define INCLUDE_CPPGC_CUSTOM_SPACE_H_ - -#include - -namespace cppgc { - -/** - * Index identifying a custom space. - */ -struct CustomSpaceIndex { - constexpr CustomSpaceIndex(size_t value) : value(value) {} // NOLINT - size_t value; -}; - -/** - * Top-level base class for custom spaces. Users must inherit from CustomSpace - * below. - */ -class CustomSpaceBase { - public: - virtual ~CustomSpaceBase() = default; - virtual CustomSpaceIndex GetCustomSpaceIndex() const = 0; - virtual bool IsCompactable() const = 0; -}; - -/** - * Base class custom spaces should directly inherit from. The class inheriting - * from `CustomSpace` must define `kSpaceIndex` as unique space index. These - * indices need for form a sequence starting at 0. - * - * Example: - * \code - * class CustomSpace1 : public CustomSpace { - * public: - * static constexpr CustomSpaceIndex kSpaceIndex = 0; - * }; - * class CustomSpace2 : public CustomSpace { - * public: - * static constexpr CustomSpaceIndex kSpaceIndex = 1; - * }; - * \endcode - */ -template -class CustomSpace : public CustomSpaceBase { - public: - /** - * Compaction is only supported on spaces that manually manage slots - * recording. - */ - static constexpr bool kSupportsCompaction = false; - - CustomSpaceIndex GetCustomSpaceIndex() const final { - return ConcreteCustomSpace::kSpaceIndex; - } - bool IsCompactable() const final { - return ConcreteCustomSpace::kSupportsCompaction; - } -}; - -/** - * User-overridable trait that allows pinning types to custom spaces. - */ -template -struct SpaceTrait { - using Space = void; -}; - -namespace internal { - -template -struct IsAllocatedOnCompactableSpaceImpl { - static constexpr bool value = CustomSpace::kSupportsCompaction; -}; - -template <> -struct IsAllocatedOnCompactableSpaceImpl { - // Non-custom spaces are by default not compactable. - static constexpr bool value = false; -}; - -template -struct IsAllocatedOnCompactableSpace { - public: - static constexpr bool value = - IsAllocatedOnCompactableSpaceImpl::Space>::value; -}; - -} // namespace internal - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_CUSTOM_SPACE_H_ diff --git a/NativeScript/napi/v8/include/cppgc/default-platform.h b/NativeScript/napi/v8/include/cppgc/default-platform.h deleted file mode 100644 index a27871cc..00000000 --- a/NativeScript/napi/v8/include/cppgc/default-platform.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ -#define INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ - -#include - -#include "cppgc/platform.h" -#include "libplatform/libplatform.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -/** - * Platform provided by cppgc. Uses V8's DefaultPlatform provided by - * libplatform internally. Exception: `GetForegroundTaskRunner()`, see below. - */ -class V8_EXPORT DefaultPlatform : public Platform { - public: - using IdleTaskSupport = v8::platform::IdleTaskSupport; - explicit DefaultPlatform( - int thread_pool_size = 0, - IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, - std::unique_ptr tracing_controller = {}) - : v8_platform_(v8::platform::NewDefaultPlatform( - thread_pool_size, idle_task_support, - v8::platform::InProcessStackDumping::kDisabled, - std::move(tracing_controller))) {} - - cppgc::PageAllocator* GetPageAllocator() override { - return v8_platform_->GetPageAllocator(); - } - - double MonotonicallyIncreasingTime() override { - return v8_platform_->MonotonicallyIncreasingTime(); - } - - std::shared_ptr GetForegroundTaskRunner() override { - // V8's default platform creates a new task runner when passed the - // `v8::Isolate` pointer the first time. For non-default platforms this will - // require getting the appropriate task runner. - return v8_platform_->GetForegroundTaskRunner(kNoIsolate); - } - - std::unique_ptr PostJob( - cppgc::TaskPriority priority, - std::unique_ptr job_task) override { - return v8_platform_->PostJob(priority, std::move(job_task)); - } - - TracingController* GetTracingController() override { - return v8_platform_->GetTracingController(); - } - - v8::Platform* GetV8Platform() const { return v8_platform_.get(); } - - protected: - static constexpr v8::Isolate* kNoIsolate = nullptr; - - std::unique_ptr v8_platform_; -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ diff --git a/NativeScript/napi/v8/include/cppgc/ephemeron-pair.h b/NativeScript/napi/v8/include/cppgc/ephemeron-pair.h deleted file mode 100644 index e16cf1f0..00000000 --- a/NativeScript/napi/v8/include/cppgc/ephemeron-pair.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_EPHEMERON_PAIR_H_ -#define INCLUDE_CPPGC_EPHEMERON_PAIR_H_ - -#include "cppgc/liveness-broker.h" -#include "cppgc/member.h" - -namespace cppgc { - -/** - * An ephemeron pair is used to conditionally retain an object. - * The `value` will be kept alive only if the `key` is alive. - */ -template -struct EphemeronPair { - EphemeronPair(K* k, V* v) : key(k), value(v) {} - WeakMember key; - Member value; - - void ClearValueIfKeyIsDead(const LivenessBroker& broker) { - if (!broker.IsHeapObjectAlive(key)) value = nullptr; - } -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_EPHEMERON_PAIR_H_ diff --git a/NativeScript/napi/v8/include/cppgc/explicit-management.h b/NativeScript/napi/v8/include/cppgc/explicit-management.h deleted file mode 100644 index 0290328d..00000000 --- a/NativeScript/napi/v8/include/cppgc/explicit-management.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ -#define INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ - -#include - -#include "cppgc/allocation.h" -#include "cppgc/internal/logging.h" -#include "cppgc/type-traits.h" - -namespace cppgc { - -class HeapHandle; - -namespace subtle { - -template -void FreeUnreferencedObject(HeapHandle& heap_handle, T& object); -template -bool Resize(T& object, AdditionalBytes additional_bytes); - -} // namespace subtle - -namespace internal { - -class ExplicitManagementImpl final { - private: - V8_EXPORT static void FreeUnreferencedObject(HeapHandle&, void*); - V8_EXPORT static bool Resize(void*, size_t); - - template - friend void subtle::FreeUnreferencedObject(HeapHandle&, T&); - template - friend bool subtle::Resize(T&, AdditionalBytes); -}; -} // namespace internal - -namespace subtle { - -/** - * Informs the garbage collector that `object` can be immediately reclaimed. The - * destructor may not be invoked immediately but only on next garbage - * collection. - * - * It is up to the embedder to guarantee that no other object holds a reference - * to `object` after calling `FreeUnreferencedObject()`. In case such a - * reference exists, it's use results in a use-after-free. - * - * To aid in using the API, `FreeUnreferencedObject()` may be called from - * destructors on objects that would be reclaimed in the same garbage collection - * cycle. - * - * \param heap_handle The corresponding heap. - * \param object Reference to an object that is of type `GarbageCollected` and - * should be immediately reclaimed. - */ -template -void FreeUnreferencedObject(HeapHandle& heap_handle, T& object) { - static_assert(IsGarbageCollectedTypeV, - "Object must be of type GarbageCollected."); - internal::ExplicitManagementImpl::FreeUnreferencedObject(heap_handle, - &object); -} - -/** - * Tries to resize `object` of type `T` with additional bytes on top of - * sizeof(T). Resizing is only useful with trailing inlined storage, see e.g. - * `MakeGarbageCollected(AllocationHandle&, AdditionalBytes)`. - * - * `Resize()` performs growing or shrinking as needed and may skip the operation - * for internal reasons, see return value. - * - * It is up to the embedder to guarantee that in case of shrinking a larger - * object down, the reclaimed area is not used anymore. Any subsequent use - * results in a use-after-free. - * - * The `object` must be live when calling `Resize()`. - * - * \param object Reference to an object that is of type `GarbageCollected` and - * should be resized. - * \param additional_bytes Bytes in addition to sizeof(T) that the object should - * provide. - * \returns true when the operation was successful and the result can be relied - * on, and false otherwise. - */ -template -bool Resize(T& object, AdditionalBytes additional_bytes) { - static_assert(IsGarbageCollectedTypeV, - "Object must be of type GarbageCollected."); - return internal::ExplicitManagementImpl::Resize( - &object, sizeof(T) + additional_bytes.value); -} - -} // namespace subtle -} // namespace cppgc - -#endif // INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ diff --git a/NativeScript/napi/v8/include/cppgc/garbage-collected.h b/NativeScript/napi/v8/include/cppgc/garbage-collected.h deleted file mode 100644 index 6737c8be..00000000 --- a/NativeScript/napi/v8/include/cppgc/garbage-collected.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ -#define INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ - -#include "cppgc/internal/api-constants.h" -#include "cppgc/platform.h" -#include "cppgc/trace-trait.h" -#include "cppgc/type-traits.h" - -namespace cppgc { - -class Visitor; - -/** - * Base class for managed objects. Only descendent types of `GarbageCollected` - * can be constructed using `MakeGarbageCollected()`. Must be inherited from as - * left-most base class. - * - * Types inheriting from GarbageCollected must provide a method of - * signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed - * pointers to the visitor and delegates to garbage-collected base classes. - * The method must be virtual if the type is not directly a child of - * GarbageCollected and marked as final. - * - * \code - * // Example using final class. - * class FinalType final : public GarbageCollected { - * public: - * void Trace(cppgc::Visitor* visitor) const { - * // Dispatch using visitor->Trace(...); - * } - * }; - * - * // Example using non-final base class. - * class NonFinalBase : public GarbageCollected { - * public: - * virtual void Trace(cppgc::Visitor*) const {} - * }; - * - * class FinalChild final : public NonFinalBase { - * public: - * void Trace(cppgc::Visitor* visitor) const final { - * // Dispatch using visitor->Trace(...); - * NonFinalBase::Trace(visitor); - * } - * }; - * \endcode - */ -template -class GarbageCollected { - public: - using IsGarbageCollectedTypeMarker = void; - using ParentMostGarbageCollectedType = T; - - // Must use MakeGarbageCollected. - void* operator new(size_t) = delete; - void* operator new[](size_t) = delete; - // The garbage collector is taking care of reclaiming the object. Also, - // virtual destructor requires an unambiguous, accessible 'operator delete'. - void operator delete(void*) { -#ifdef V8_ENABLE_CHECKS - internal::Fatal( - "Manually deleting a garbage collected object is not allowed"); -#endif // V8_ENABLE_CHECKS - } - void operator delete[](void*) = delete; - - protected: - GarbageCollected() = default; -}; - -/** - * Base class for managed mixin objects. Such objects cannot be constructed - * directly but must be mixed into the inheritance hierarchy of a - * GarbageCollected object. - * - * Types inheriting from GarbageCollectedMixin must override a virtual method - * of signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed - * pointers to the visitor and delegates to base classes. - * - * \code - * class Mixin : public GarbageCollectedMixin { - * public: - * void Trace(cppgc::Visitor* visitor) const override { - * // Dispatch using visitor->Trace(...); - * } - * }; - * \endcode - */ -class GarbageCollectedMixin { - public: - using IsGarbageCollectedMixinTypeMarker = void; - - /** - * This Trace method must be overriden by objects inheriting from - * GarbageCollectedMixin. - */ - virtual void Trace(cppgc::Visitor*) const {} -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ diff --git a/NativeScript/napi/v8/include/cppgc/heap-consistency.h b/NativeScript/napi/v8/include/cppgc/heap-consistency.h deleted file mode 100644 index eb7fdaee..00000000 --- a/NativeScript/napi/v8/include/cppgc/heap-consistency.h +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_HEAP_CONSISTENCY_H_ -#define INCLUDE_CPPGC_HEAP_CONSISTENCY_H_ - -#include - -#include "cppgc/internal/write-barrier.h" -#include "cppgc/macros.h" -#include "cppgc/member.h" -#include "cppgc/trace-trait.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -class HeapHandle; - -namespace subtle { - -/** - * **DO NOT USE: Use the appropriate managed types.** - * - * Consistency helpers that aid in maintaining a consistent internal state of - * the garbage collector. - */ -class HeapConsistency final { - public: - using WriteBarrierParams = internal::WriteBarrier::Params; - using WriteBarrierType = internal::WriteBarrier::Type; - - /** - * Gets the required write barrier type for a specific write. - * - * \param slot Slot containing the pointer to the object. The slot itself - * must reside in an object that has been allocated using - * `MakeGarbageCollected()`. - * \param value The pointer to the object. May be an interior pointer to an - * interface of the actual object. - * \param params Parameters that may be used for actual write barrier calls. - * Only filled if return value indicates that a write barrier is needed. The - * contents of the `params` are an implementation detail. - * \returns whether a write barrier is needed and which barrier to invoke. - */ - static V8_INLINE WriteBarrierType GetWriteBarrierType( - const void* slot, const void* value, WriteBarrierParams& params) { - return internal::WriteBarrier::GetWriteBarrierType(slot, value, params); - } - - /** - * Gets the required write barrier type for a specific write. This override is - * only used for all the BasicMember types. - * - * \param slot Slot containing the pointer to the object. The slot itself - * must reside in an object that has been allocated using - * `MakeGarbageCollected()`. - * \param value The pointer to the object held via `BasicMember`. - * \param params Parameters that may be used for actual write barrier calls. - * Only filled if return value indicates that a write barrier is needed. The - * contents of the `params` are an implementation detail. - * \returns whether a write barrier is needed and which barrier to invoke. - */ - template - static V8_INLINE WriteBarrierType GetWriteBarrierType( - const internal::BasicMember& value, - WriteBarrierParams& params) { - return internal::WriteBarrier::GetWriteBarrierType( - value.GetRawSlot(), value.GetRawStorage(), params); - } - - /** - * Gets the required write barrier type for a specific write. - * - * \param slot Slot to some part of an object. The object must not necessarily - have been allocated using `MakeGarbageCollected()` but can also live - off-heap or on stack. - * \param params Parameters that may be used for actual write barrier calls. - * Only filled if return value indicates that a write barrier is needed. The - * contents of the `params` are an implementation detail. - * \param callback Callback returning the corresponding heap handle. The - * callback is only invoked if the heap cannot otherwise be figured out. The - * callback must not allocate. - * \returns whether a write barrier is needed and which barrier to invoke. - */ - template - static V8_INLINE WriteBarrierType - GetWriteBarrierType(const void* slot, WriteBarrierParams& params, - HeapHandleCallback callback) { - return internal::WriteBarrier::GetWriteBarrierType(slot, params, callback); - } - - /** - * Gets the required write barrier type for a specific write. - * This version is meant to be used in conjunction with with a marking write - * barrier barrier which doesn't consider the slot. - * - * \param value The pointer to the object. May be an interior pointer to an - * interface of the actual object. - * \param params Parameters that may be used for actual write barrier calls. - * Only filled if return value indicates that a write barrier is needed. The - * contents of the `params` are an implementation detail. - * \returns whether a write barrier is needed and which barrier to invoke. - */ - static V8_INLINE WriteBarrierType - GetWriteBarrierType(const void* value, WriteBarrierParams& params) { - return internal::WriteBarrier::GetWriteBarrierType(value, params); - } - - /** - * Conservative Dijkstra-style write barrier that processes an object if it - * has not yet been processed. - * - * \param params The parameters retrieved from `GetWriteBarrierType()`. - * \param object The pointer to the object. May be an interior pointer to a - * an interface of the actual object. - */ - static V8_INLINE void DijkstraWriteBarrier(const WriteBarrierParams& params, - const void* object) { - internal::WriteBarrier::DijkstraMarkingBarrier(params, object); - } - - /** - * Conservative Dijkstra-style write barrier that processes a range of - * elements if they have not yet been processed. - * - * \param params The parameters retrieved from `GetWriteBarrierType()`. - * \param first_element Pointer to the first element that should be processed. - * The slot itself must reside in an object that has been allocated using - * `MakeGarbageCollected()`. - * \param element_size Size of the element in bytes. - * \param number_of_elements Number of elements that should be processed, - * starting with `first_element`. - * \param trace_callback The trace callback that should be invoked for each - * element if necessary. - */ - static V8_INLINE void DijkstraWriteBarrierRange( - const WriteBarrierParams& params, const void* first_element, - size_t element_size, size_t number_of_elements, - TraceCallback trace_callback) { - internal::WriteBarrier::DijkstraMarkingBarrierRange( - params, first_element, element_size, number_of_elements, - trace_callback); - } - - /** - * Steele-style write barrier that re-processes an object if it has already - * been processed. - * - * \param params The parameters retrieved from `GetWriteBarrierType()`. - * \param object The pointer to the object which must point to an object that - * has been allocated using `MakeGarbageCollected()`. Interior pointers are - * not supported. - */ - static V8_INLINE void SteeleWriteBarrier(const WriteBarrierParams& params, - const void* object) { - internal::WriteBarrier::SteeleMarkingBarrier(params, object); - } - - /** - * Generational barrier for maintaining consistency when running with multiple - * generations. - * - * \param params The parameters retrieved from `GetWriteBarrierType()`. - * \param slot Slot containing the pointer to the object. The slot itself - * must reside in an object that has been allocated using - * `MakeGarbageCollected()`. - */ - static V8_INLINE void GenerationalBarrier(const WriteBarrierParams& params, - const void* slot) { - internal::WriteBarrier::GenerationalBarrier< - internal::WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, - slot); - } - - /** - * Generational barrier for maintaining consistency when running with multiple - * generations. This version is used when slot contains uncompressed pointer. - * - * \param params The parameters retrieved from `GetWriteBarrierType()`. - * \param slot Uncompressed slot containing the direct pointer to the object. - * The slot itself must reside in an object that has been allocated using - * `MakeGarbageCollected()`. - */ - static V8_INLINE void GenerationalBarrierForUncompressedSlot( - const WriteBarrierParams& params, const void* uncompressed_slot) { - internal::WriteBarrier::GenerationalBarrier< - internal::WriteBarrier::GenerationalBarrierType:: - kPreciseUncompressedSlot>(params, uncompressed_slot); - } - - /** - * Generational barrier for source object that may contain outgoing pointers - * to objects in young generation. - * - * \param params The parameters retrieved from `GetWriteBarrierType()`. - * \param inner_pointer Pointer to the source object. - */ - static V8_INLINE void GenerationalBarrierForSourceObject( - const WriteBarrierParams& params, const void* inner_pointer) { - internal::WriteBarrier::GenerationalBarrier< - internal::WriteBarrier::GenerationalBarrierType::kImpreciseSlot>( - params, inner_pointer); - } - - private: - HeapConsistency() = delete; -}; - -/** - * Disallows garbage collection finalizations. Any garbage collection triggers - * result in a crash when in this scope. - * - * Note that the garbage collector already covers paths that can lead to garbage - * collections, so user code does not require checking - * `IsGarbageCollectionAllowed()` before allocations. - */ -class V8_EXPORT V8_NODISCARD DisallowGarbageCollectionScope final { - CPPGC_STACK_ALLOCATED(); - - public: - /** - * \returns whether garbage collections are currently allowed. - */ - static bool IsGarbageCollectionAllowed(HeapHandle& heap_handle); - - /** - * Enters a disallow garbage collection scope. Must be paired with `Leave()`. - * Prefer a scope instance of `DisallowGarbageCollectionScope`. - * - * \param heap_handle The corresponding heap. - */ - static void Enter(HeapHandle& heap_handle); - - /** - * Leaves a disallow garbage collection scope. Must be paired with `Enter()`. - * Prefer a scope instance of `DisallowGarbageCollectionScope`. - * - * \param heap_handle The corresponding heap. - */ - static void Leave(HeapHandle& heap_handle); - - /** - * Constructs a scoped object that automatically enters and leaves a disallow - * garbage collection scope based on its lifetime. - * - * \param heap_handle The corresponding heap. - */ - explicit DisallowGarbageCollectionScope(HeapHandle& heap_handle); - ~DisallowGarbageCollectionScope(); - - DisallowGarbageCollectionScope(const DisallowGarbageCollectionScope&) = - delete; - DisallowGarbageCollectionScope& operator=( - const DisallowGarbageCollectionScope&) = delete; - - private: - HeapHandle& heap_handle_; -}; - -/** - * Avoids invoking garbage collection finalizations. Already running garbage - * collection phase are unaffected by this scope. - * - * Should only be used temporarily as the scope has an impact on memory usage - * and follow up garbage collections. - */ -class V8_EXPORT V8_NODISCARD NoGarbageCollectionScope final { - CPPGC_STACK_ALLOCATED(); - - public: - /** - * Enters a no garbage collection scope. Must be paired with `Leave()`. Prefer - * a scope instance of `NoGarbageCollectionScope`. - * - * \param heap_handle The corresponding heap. - */ - static void Enter(HeapHandle& heap_handle); - - /** - * Leaves a no garbage collection scope. Must be paired with `Enter()`. Prefer - * a scope instance of `NoGarbageCollectionScope`. - * - * \param heap_handle The corresponding heap. - */ - static void Leave(HeapHandle& heap_handle); - - /** - * Constructs a scoped object that automatically enters and leaves a no - * garbage collection scope based on its lifetime. - * - * \param heap_handle The corresponding heap. - */ - explicit NoGarbageCollectionScope(HeapHandle& heap_handle); - ~NoGarbageCollectionScope(); - - NoGarbageCollectionScope(const NoGarbageCollectionScope&) = delete; - NoGarbageCollectionScope& operator=(const NoGarbageCollectionScope&) = delete; - - private: - HeapHandle& heap_handle_; -}; - -} // namespace subtle -} // namespace cppgc - -#endif // INCLUDE_CPPGC_HEAP_CONSISTENCY_H_ diff --git a/NativeScript/napi/v8/include/cppgc/heap-handle.h b/NativeScript/napi/v8/include/cppgc/heap-handle.h deleted file mode 100644 index 0d1d21e6..00000000 --- a/NativeScript/napi/v8/include/cppgc/heap-handle.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2022 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_HEAP_HANDLE_H_ -#define INCLUDE_CPPGC_HEAP_HANDLE_H_ - -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -namespace internal { -class HeapBase; -class WriteBarrierTypeForCagedHeapPolicy; -class WriteBarrierTypeForNonCagedHeapPolicy; -} // namespace internal - -/** - * Opaque handle used for additional heap APIs. - */ -class HeapHandle { - public: - // Deleted copy ctor to avoid treating the type by value. - HeapHandle(const HeapHandle&) = delete; - HeapHandle& operator=(const HeapHandle&) = delete; - - private: - HeapHandle() = default; - - V8_INLINE bool is_incremental_marking_in_progress() const { - return is_incremental_marking_in_progress_; - } - - V8_INLINE bool is_young_generation_enabled() const { - return is_young_generation_enabled_; - } - - bool is_incremental_marking_in_progress_ = false; - bool is_young_generation_enabled_ = false; - - friend class internal::HeapBase; - friend class internal::WriteBarrierTypeForCagedHeapPolicy; - friend class internal::WriteBarrierTypeForNonCagedHeapPolicy; -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_HEAP_HANDLE_H_ diff --git a/NativeScript/napi/v8/include/cppgc/heap-state.h b/NativeScript/napi/v8/include/cppgc/heap-state.h deleted file mode 100644 index 28212589..00000000 --- a/NativeScript/napi/v8/include/cppgc/heap-state.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_HEAP_STATE_H_ -#define INCLUDE_CPPGC_HEAP_STATE_H_ - -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -class HeapHandle; - -namespace subtle { - -/** - * Helpers to peek into heap-internal state. - */ -class V8_EXPORT HeapState final { - public: - /** - * Returns whether the garbage collector is marking. This API is experimental - * and is expected to be removed in future. - * - * \param heap_handle The corresponding heap. - * \returns true if the garbage collector is currently marking, and false - * otherwise. - */ - static bool IsMarking(const HeapHandle& heap_handle); - - /* - * Returns whether the garbage collector is sweeping. This API is experimental - * and is expected to be removed in future. - * - * \param heap_handle The corresponding heap. - * \returns true if the garbage collector is currently sweeping, and false - * otherwise. - */ - static bool IsSweeping(const HeapHandle& heap_handle); - - /* - * Returns whether the garbage collector is currently sweeping on the thread - * owning this heap. This API allows the caller to determine whether it has - * been called from a destructor of a managed object. This API is experimental - * and may be removed in future. - * - * \param heap_handle The corresponding heap. - * \returns true if the garbage collector is currently sweeping on this - * thread, and false otherwise. - */ - static bool IsSweepingOnOwningThread(const HeapHandle& heap_handle); - - /** - * Returns whether the garbage collector is in the atomic pause, i.e., the - * mutator is stopped from running. This API is experimental and is expected - * to be removed in future. - * - * \param heap_handle The corresponding heap. - * \returns true if the garbage collector is currently in the atomic pause, - * and false otherwise. - */ - static bool IsInAtomicPause(const HeapHandle& heap_handle); - - /** - * Returns whether the last garbage collection was finalized conservatively - * (i.e., with a non-empty stack). This API is experimental and is expected to - * be removed in future. - * - * \param heap_handle The corresponding heap. - * \returns true if the last garbage collection was finalized conservatively, - * and false otherwise. - */ - static bool PreviousGCWasConservative(const HeapHandle& heap_handle); - - private: - HeapState() = delete; -}; - -} // namespace subtle -} // namespace cppgc - -#endif // INCLUDE_CPPGC_HEAP_STATE_H_ diff --git a/NativeScript/napi/v8/include/cppgc/heap-statistics.h b/NativeScript/napi/v8/include/cppgc/heap-statistics.h deleted file mode 100644 index 5e389874..00000000 --- a/NativeScript/napi/v8/include/cppgc/heap-statistics.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_HEAP_STATISTICS_H_ -#define INCLUDE_CPPGC_HEAP_STATISTICS_H_ - -#include -#include -#include -#include - -namespace cppgc { - -/** - * `HeapStatistics` contains memory consumption and utilization statistics for a - * cppgc heap. - */ -struct HeapStatistics final { - /** - * Specifies the detail level of the heap statistics. Brief statistics contain - * only the top-level allocated and used memory statistics for the entire - * heap. Detailed statistics also contain a break down per space and page, as - * well as freelist statistics and object type histograms. Note that used - * memory reported by brief statistics and detailed statistics might differ - * slightly. - */ - enum DetailLevel : uint8_t { - kBrief, - kDetailed, - }; - - /** - * Object statistics for a single type. - */ - struct ObjectStatsEntry { - /** - * Number of allocated bytes. - */ - size_t allocated_bytes; - /** - * Number of allocated objects. - */ - size_t object_count; - }; - - /** - * Page granularity statistics. For each page the statistics record the - * allocated memory size and overall used memory size for the page. - */ - struct PageStatistics { - /** Overall committed amount of memory for the page. */ - size_t committed_size_bytes = 0; - /** Resident amount of memory held by the page. */ - size_t resident_size_bytes = 0; - /** Amount of memory actually used on the page. */ - size_t used_size_bytes = 0; - /** Statistics for object allocated on the page. Filled only when - * NameProvider::SupportsCppClassNamesAsObjectNames() is true. */ - std::vector object_statistics; - }; - - /** - * Statistics of the freelist (used only in non-large object spaces). For - * each bucket in the freelist the statistics record the bucket size, the - * number of freelist entries in the bucket, and the overall allocated memory - * consumed by these freelist entries. - */ - struct FreeListStatistics { - /** bucket sizes in the freelist. */ - std::vector bucket_size; - /** number of freelist entries per bucket. */ - std::vector free_count; - /** memory size consumed by freelist entries per size. */ - std::vector free_size; - }; - - /** - * Space granularity statistics. For each space the statistics record the - * space name, the amount of allocated memory and overall used memory for the - * space. The statistics also contain statistics for each of the space's - * pages, its freelist and the objects allocated on the space. - */ - struct SpaceStatistics { - /** The space name */ - std::string name; - /** Overall committed amount of memory for the heap. */ - size_t committed_size_bytes = 0; - /** Resident amount of memory held by the heap. */ - size_t resident_size_bytes = 0; - /** Amount of memory actually used on the space. */ - size_t used_size_bytes = 0; - /** Statistics for each of the pages in the space. */ - std::vector page_stats; - /** Statistics for the freelist of the space. */ - FreeListStatistics free_list_stats; - }; - - /** Overall committed amount of memory for the heap. */ - size_t committed_size_bytes = 0; - /** Resident amount of memory held by the heap. */ - size_t resident_size_bytes = 0; - /** Amount of memory actually used on the heap. */ - size_t used_size_bytes = 0; - /** Detail level of this HeapStatistics. */ - DetailLevel detail_level; - - /** Statistics for each of the spaces in the heap. Filled only when - * `detail_level` is `DetailLevel::kDetailed`. */ - std::vector space_stats; - - /** - * Vector of `cppgc::GarbageCollected` type names. - */ - std::vector type_names; -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_HEAP_STATISTICS_H_ diff --git a/NativeScript/napi/v8/include/cppgc/heap.h b/NativeScript/napi/v8/include/cppgc/heap.h deleted file mode 100644 index 02ee12ea..00000000 --- a/NativeScript/napi/v8/include/cppgc/heap.h +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_HEAP_H_ -#define INCLUDE_CPPGC_HEAP_H_ - -#include -#include -#include -#include - -#include "cppgc/common.h" -#include "cppgc/custom-space.h" -#include "cppgc/platform.h" -#include "v8config.h" // NOLINT(build/include_directory) - -/** - * cppgc - A C++ garbage collection library. - */ -namespace cppgc { - -class AllocationHandle; -class HeapHandle; - -/** - * Implementation details of cppgc. Those details are considered internal and - * may change at any point in time without notice. Users should never rely on - * the contents of this namespace. - */ -namespace internal { -class Heap; -} // namespace internal - -class V8_EXPORT Heap { - public: - /** - * Specifies the stack state the embedder is in. - */ - using StackState = EmbedderStackState; - - /** - * Specifies whether conservative stack scanning is supported. - */ - enum class StackSupport : uint8_t { - /** - * Conservative stack scan is supported. - */ - kSupportsConservativeStackScan, - /** - * Conservative stack scan is not supported. Embedders may use this option - * when using custom infrastructure that is unsupported by the library. - */ - kNoConservativeStackScan, - }; - - /** - * Specifies supported marking types. - */ - enum class MarkingType : uint8_t { - /** - * Atomic stop-the-world marking. This option does not require any write - * barriers but is the most intrusive in terms of jank. - */ - kAtomic, - /** - * Incremental marking interleaves marking with the rest of the application - * workload on the same thread. - */ - kIncremental, - /** - * Incremental and concurrent marking. - */ - kIncrementalAndConcurrent - }; - - /** - * Specifies supported sweeping types. - */ - enum class SweepingType : uint8_t { - /** - * Atomic stop-the-world sweeping. All of sweeping is performed at once. - */ - kAtomic, - /** - * Incremental sweeping interleaves sweeping with the rest of the - * application workload on the same thread. - */ - kIncremental, - /** - * Incremental and concurrent sweeping. Sweeping is split and interleaved - * with the rest of the application. - */ - kIncrementalAndConcurrent - }; - - /** - * Constraints for a Heap setup. - */ - struct ResourceConstraints { - /** - * Allows the heap to grow to some initial size in bytes before triggering - * garbage collections. This is useful when it is known that applications - * need a certain minimum heap to run to avoid repeatedly invoking the - * garbage collector when growing the heap. - */ - size_t initial_heap_size_bytes = 0; - }; - - /** - * Options specifying Heap properties (e.g. custom spaces) when initializing a - * heap through `Heap::Create()`. - */ - struct HeapOptions { - /** - * Creates reasonable defaults for instantiating a Heap. - * - * \returns the HeapOptions that can be passed to `Heap::Create()`. - */ - static HeapOptions Default() { return {}; } - - /** - * Custom spaces added to heap are required to have indices forming a - * numbered sequence starting at 0, i.e., their `kSpaceIndex` must - * correspond to the index they reside in the vector. - */ - std::vector> custom_spaces; - - /** - * Specifies whether conservative stack scan is supported. When conservative - * stack scan is not supported, the collector may try to invoke - * garbage collections using non-nestable task, which are guaranteed to have - * no interesting stack, through the provided Platform. If such tasks are - * not supported by the Platform, the embedder must take care of invoking - * the GC through `ForceGarbageCollectionSlow()`. - */ - StackSupport stack_support = StackSupport::kSupportsConservativeStackScan; - - /** - * Specifies which types of marking are supported by the heap. - */ - MarkingType marking_support = MarkingType::kIncrementalAndConcurrent; - - /** - * Specifies which types of sweeping are supported by the heap. - */ - SweepingType sweeping_support = SweepingType::kIncrementalAndConcurrent; - - /** - * Resource constraints specifying various properties that the internal - * GC scheduler follows. - */ - ResourceConstraints resource_constraints; - }; - - /** - * Creates a new heap that can be used for object allocation. - * - * \param platform implemented and provided by the embedder. - * \param options HeapOptions specifying various properties for the Heap. - * \returns a new Heap instance. - */ - static std::unique_ptr Create( - std::shared_ptr platform, - HeapOptions options = HeapOptions::Default()); - - virtual ~Heap() = default; - - /** - * Forces garbage collection. - * - * \param source String specifying the source (or caller) triggering a - * forced garbage collection. - * \param reason String specifying the reason for the forced garbage - * collection. - * \param stack_state The embedder stack state, see StackState. - */ - void ForceGarbageCollectionSlow( - const char* source, const char* reason, - StackState stack_state = StackState::kMayContainHeapPointers); - - /** - * \returns the opaque handle for allocating objects using - * `MakeGarbageCollected()`. - */ - AllocationHandle& GetAllocationHandle(); - - /** - * \returns the opaque heap handle which may be used to refer to this heap in - * other APIs. Valid as long as the underlying `Heap` is alive. - */ - HeapHandle& GetHeapHandle(); - - private: - Heap() = default; - - friend class internal::Heap; -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_HEAP_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/api-constants.h b/NativeScript/napi/v8/include/cppgc/internal/api-constants.h deleted file mode 100644 index 4e2a637e..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/api-constants.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ -#define INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ - -#include -#include - -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -// Embedders should not rely on this code! - -// Internal constants to avoid exposing internal types on the API surface. -namespace api_constants { - -constexpr size_t kKB = 1024; -constexpr size_t kMB = kKB * 1024; -constexpr size_t kGB = kMB * 1024; - -// Offset of the uint16_t bitfield from the payload contaning the -// in-construction bit. This is subtracted from the payload pointer to get -// to the right bitfield. -static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload = - 2 * sizeof(uint16_t); -// Mask for in-construction bit. -static constexpr uint16_t kFullyConstructedBitMask = uint16_t{1}; - -static constexpr size_t kPageSize = size_t{1} << 17; - -#if defined(V8_TARGET_ARCH_ARM64) && defined(V8_OS_DARWIN) -constexpr size_t kGuardPageSize = 0; -#else -constexpr size_t kGuardPageSize = 4096; -#endif - -static constexpr size_t kLargeObjectSizeThreshold = kPageSize / 2; - -#if defined(CPPGC_POINTER_COMPRESSION) -#if defined(CPPGC_ENABLE_LARGER_CAGE) -constexpr unsigned kPointerCompressionShift = 3; -#else // !defined(CPPGC_ENABLE_LARGER_CAGE) -constexpr unsigned kPointerCompressionShift = 1; -#endif // !defined(CPPGC_ENABLE_LARGER_CAGE) -#endif // !defined(CPPGC_POINTER_COMPRESSION) - -#if defined(CPPGC_CAGED_HEAP) -#if defined(CPPGC_2GB_CAGE) -constexpr size_t kCagedHeapDefaultReservationSize = - static_cast(2) * kGB; -constexpr size_t kCagedHeapMaxReservationSize = - kCagedHeapDefaultReservationSize; -#else // !defined(CPPGC_2GB_CAGE) -constexpr size_t kCagedHeapDefaultReservationSize = - static_cast(4) * kGB; -#if defined(CPPGC_POINTER_COMPRESSION) -constexpr size_t kCagedHeapMaxReservationSize = - size_t{1} << (31 + kPointerCompressionShift); -#else // !defined(CPPGC_POINTER_COMPRESSION) -constexpr size_t kCagedHeapMaxReservationSize = - kCagedHeapDefaultReservationSize; -#endif // !defined(CPPGC_POINTER_COMPRESSION) -#endif // !defined(CPPGC_2GB_CAGE) -constexpr size_t kCagedHeapReservationAlignment = kCagedHeapMaxReservationSize; -#endif // defined(CPPGC_CAGED_HEAP) - -static constexpr size_t kDefaultAlignment = sizeof(void*); - -// Maximum support alignment for a type as in `alignof(T)`. -static constexpr size_t kMaxSupportedAlignment = 2 * kDefaultAlignment; - -// Granularity of heap allocations. -constexpr size_t kAllocationGranularity = sizeof(void*); - -// Default cacheline size. -constexpr size_t kCachelineSize = 64; - -} // namespace api_constants - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/atomic-entry-flag.h b/NativeScript/napi/v8/include/cppgc/internal/atomic-entry-flag.h deleted file mode 100644 index 5a7d3b8f..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/atomic-entry-flag.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_ -#define INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_ - -#include - -namespace cppgc { -namespace internal { - -// A flag which provides a fast check whether a scope may be entered on the -// current thread, without needing to access thread-local storage or mutex. Can -// have false positives (i.e., spuriously report that it might be entered), so -// it is expected that this will be used in tandem with a precise check that the -// scope is in fact entered on that thread. -// -// Example: -// g_frobnicating_flag.MightBeEntered() && -// ThreadLocalFrobnicator().IsFrobnicating() -// -// Relaxed atomic operations are sufficient, since: -// - all accesses remain atomic -// - each thread must observe its own operations in order -// - no thread ever exits the flag more times than it enters (if used correctly) -// And so if a thread observes zero, it must be because it has observed an equal -// number of exits as entries. -class AtomicEntryFlag final { - public: - void Enter() { entries_.fetch_add(1, std::memory_order_relaxed); } - void Exit() { entries_.fetch_sub(1, std::memory_order_relaxed); } - - // Returns false only if the current thread is not between a call to Enter - // and a call to Exit. Returns true if this thread or another thread may - // currently be in the scope guarded by this flag. - bool MightBeEntered() const { - return entries_.load(std::memory_order_relaxed) != 0; - } - - private: - std::atomic_int entries_{0}; -}; - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/base-page-handle.h b/NativeScript/napi/v8/include/cppgc/internal/base-page-handle.h deleted file mode 100644 index 9c690755..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/base-page-handle.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ -#define INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ - -#include "cppgc/heap-handle.h" -#include "cppgc/internal/api-constants.h" -#include "cppgc/internal/logging.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -// The class is needed in the header to allow for fast access to HeapHandle in -// the write barrier. -class BasePageHandle { - public: - static V8_INLINE BasePageHandle* FromPayload(void* payload) { - return reinterpret_cast( - (reinterpret_cast(payload) & - ~(api_constants::kPageSize - 1)) + - api_constants::kGuardPageSize); - } - static V8_INLINE const BasePageHandle* FromPayload(const void* payload) { - return FromPayload(const_cast(payload)); - } - - HeapHandle& heap_handle() { return heap_handle_; } - const HeapHandle& heap_handle() const { return heap_handle_; } - - protected: - explicit BasePageHandle(HeapHandle& heap_handle) : heap_handle_(heap_handle) { - CPPGC_DCHECK(reinterpret_cast(this) % api_constants::kPageSize == - api_constants::kGuardPageSize); - } - - HeapHandle& heap_handle_; -}; - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/caged-heap-local-data.h b/NativeScript/napi/v8/include/cppgc/internal/caged-heap-local-data.h deleted file mode 100644 index 1eb87dfb..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/caged-heap-local-data.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ -#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ - -#include -#include -#include - -#include "cppgc/internal/api-constants.h" -#include "cppgc/internal/caged-heap.h" -#include "cppgc/internal/logging.h" -#include "cppgc/platform.h" -#include "v8config.h" // NOLINT(build/include_directory) - -#if __cpp_lib_bitopts -#include -#endif // __cpp_lib_bitopts - -#if defined(CPPGC_CAGED_HEAP) - -namespace cppgc { -namespace internal { - -class HeapBase; -class HeapBaseHandle; - -#if defined(CPPGC_YOUNG_GENERATION) - -// AgeTable is the bytemap needed for the fast generation check in the write -// barrier. AgeTable contains entries that correspond to 4096 bytes memory -// regions (cards). Each entry in the table represents generation of the objects -// that reside on the corresponding card (young, old or mixed). -class V8_EXPORT AgeTable final { - static constexpr size_t kRequiredSize = 1 * api_constants::kMB; - static constexpr size_t kAllocationGranularity = - api_constants::kAllocationGranularity; - - public: - // Represents age of the objects living on a single card. - enum class Age : uint8_t { kOld, kYoung, kMixed }; - // When setting age for a range, consider or ignore ages of the adjacent - // cards. - enum class AdjacentCardsPolicy : uint8_t { kConsider, kIgnore }; - - static constexpr size_t kCardSizeInBytes = - api_constants::kCagedHeapDefaultReservationSize / kRequiredSize; - - static constexpr size_t CalculateAgeTableSizeForHeapSize(size_t heap_size) { - return heap_size / kCardSizeInBytes; - } - - void SetAge(uintptr_t cage_offset, Age age) { - table_[card(cage_offset)] = age; - } - - V8_INLINE Age GetAge(uintptr_t cage_offset) const { - return table_[card(cage_offset)]; - } - - void SetAgeForRange(uintptr_t cage_offset_begin, uintptr_t cage_offset_end, - Age age, AdjacentCardsPolicy adjacent_cards_policy); - - Age GetAgeForRange(uintptr_t cage_offset_begin, - uintptr_t cage_offset_end) const; - - void ResetForTesting(); - - private: - V8_INLINE size_t card(uintptr_t offset) const { - constexpr size_t kGranularityBits = -#if __cpp_lib_bitopts - std::countr_zero(static_cast(kCardSizeInBytes)); -#elif V8_HAS_BUILTIN_CTZ - __builtin_ctz(static_cast(kCardSizeInBytes)); -#else //! V8_HAS_BUILTIN_CTZ - // Hardcode and check with assert. -#if defined(CPPGC_2GB_CAGE) - 11; -#else // !defined(CPPGC_2GB_CAGE) - 12; -#endif // !defined(CPPGC_2GB_CAGE) -#endif // !V8_HAS_BUILTIN_CTZ - static_assert((1 << kGranularityBits) == kCardSizeInBytes); - const size_t entry = offset >> kGranularityBits; - CPPGC_DCHECK(CagedHeapBase::GetAgeTableSize() > entry); - return entry; - } - -#if defined(V8_CC_GNU) - // gcc disallows flexible arrays in otherwise empty classes. - Age table_[0]; -#else // !defined(V8_CC_GNU) - Age table_[]; -#endif // !defined(V8_CC_GNU) -}; - -#endif // CPPGC_YOUNG_GENERATION - -struct CagedHeapLocalData final { - V8_INLINE static CagedHeapLocalData& Get() { - return *reinterpret_cast(CagedHeapBase::GetBase()); - } - - static constexpr size_t CalculateLocalDataSizeForHeapSize(size_t heap_size) { - return AgeTable::CalculateAgeTableSizeForHeapSize(heap_size); - } - -#if defined(CPPGC_YOUNG_GENERATION) - AgeTable age_table; -#endif -}; - -} // namespace internal -} // namespace cppgc - -#endif // defined(CPPGC_CAGED_HEAP) - -#endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/caged-heap.h b/NativeScript/napi/v8/include/cppgc/internal/caged-heap.h deleted file mode 100644 index 0c987a95..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/caged-heap.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2022 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ -#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ - -#include -#include - -#include "cppgc/internal/api-constants.h" -#include "cppgc/internal/base-page-handle.h" -#include "v8config.h" // NOLINT(build/include_directory) - -#if defined(CPPGC_CAGED_HEAP) - -namespace cppgc { -namespace internal { - -class V8_EXPORT CagedHeapBase { - public: - V8_INLINE static uintptr_t OffsetFromAddress(const void* address) { - return reinterpret_cast(address) & - (api_constants::kCagedHeapReservationAlignment - 1); - } - - V8_INLINE static bool IsWithinCage(const void* address) { - CPPGC_DCHECK(g_heap_base_); - return (reinterpret_cast(address) & - ~(api_constants::kCagedHeapReservationAlignment - 1)) == - g_heap_base_; - } - - V8_INLINE static bool AreWithinCage(const void* addr1, const void* addr2) { -#if defined(CPPGC_2GB_CAGE) - static constexpr size_t kHeapBaseShift = sizeof(uint32_t) * CHAR_BIT - 1; -#else //! defined(CPPGC_2GB_CAGE) -#if defined(CPPGC_POINTER_COMPRESSION) - static constexpr size_t kHeapBaseShift = - 31 + api_constants::kPointerCompressionShift; -#else // !defined(CPPGC_POINTER_COMPRESSION) - static constexpr size_t kHeapBaseShift = sizeof(uint32_t) * CHAR_BIT; -#endif // !defined(CPPGC_POINTER_COMPRESSION) -#endif //! defined(CPPGC_2GB_CAGE) - static_assert((static_cast(1) << kHeapBaseShift) == - api_constants::kCagedHeapMaxReservationSize); - CPPGC_DCHECK(g_heap_base_); - return !(((reinterpret_cast(addr1) ^ g_heap_base_) | - (reinterpret_cast(addr2) ^ g_heap_base_)) >> - kHeapBaseShift); - } - - V8_INLINE static uintptr_t GetBase() { return g_heap_base_; } - V8_INLINE static size_t GetAgeTableSize() { return g_age_table_size_; } - - private: - friend class CagedHeap; - - static uintptr_t g_heap_base_; - static size_t g_age_table_size_; -}; - -} // namespace internal -} // namespace cppgc - -#endif // defined(CPPGC_CAGED_HEAP) - -#endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/compiler-specific.h b/NativeScript/napi/v8/include/cppgc/internal/compiler-specific.h deleted file mode 100644 index 595b6398..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/compiler-specific.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ -#define INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ - -namespace cppgc { - -#if defined(__has_attribute) -#define CPPGC_HAS_ATTRIBUTE(FEATURE) __has_attribute(FEATURE) -#else -#define CPPGC_HAS_ATTRIBUTE(FEATURE) 0 -#endif - -#if defined(__has_cpp_attribute) -#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) __has_cpp_attribute(FEATURE) -#else -#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) 0 -#endif - -// [[no_unique_address]] comes in C++20 but supported in clang with -std >= -// c++11. -#if CPPGC_HAS_CPP_ATTRIBUTE(no_unique_address) -#define CPPGC_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else -#define CPPGC_NO_UNIQUE_ADDRESS -#endif - -#if CPPGC_HAS_ATTRIBUTE(unused) -#define CPPGC_UNUSED __attribute__((unused)) -#else -#define CPPGC_UNUSED -#endif - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/finalizer-trait.h b/NativeScript/napi/v8/include/cppgc/internal/finalizer-trait.h deleted file mode 100644 index ab49af87..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/finalizer-trait.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ -#define INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ - -#include - -#include "cppgc/type-traits.h" - -namespace cppgc { -namespace internal { - -using FinalizationCallback = void (*)(void*); - -template -struct HasFinalizeGarbageCollectedObject : std::false_type {}; - -template -struct HasFinalizeGarbageCollectedObject< - T, - std::void_t().FinalizeGarbageCollectedObject())>> - : std::true_type {}; - -// The FinalizerTraitImpl specifies how to finalize objects. -template -struct FinalizerTraitImpl; - -template -struct FinalizerTraitImpl { - private: - // Dispatch to custom FinalizeGarbageCollectedObject(). - struct Custom { - static void Call(void* obj) { - static_cast(obj)->FinalizeGarbageCollectedObject(); - } - }; - - // Dispatch to regular destructor. - struct Destructor { - static void Call(void* obj) { static_cast(obj)->~T(); } - }; - - using FinalizeImpl = - std::conditional_t::value, Custom, - Destructor>; - - public: - static void Finalize(void* obj) { - static_assert(sizeof(T), "T must be fully defined"); - FinalizeImpl::Call(obj); - } -}; - -template -struct FinalizerTraitImpl { - static void Finalize(void* obj) { - static_assert(sizeof(T), "T must be fully defined"); - } -}; - -// The FinalizerTrait is used to determine if a type requires finalization and -// what finalization means. -template -struct FinalizerTrait { - private: - // Object has a finalizer if it has - // - a custom FinalizeGarbageCollectedObject method, or - // - a destructor. - static constexpr bool kNonTrivialFinalizer = - internal::HasFinalizeGarbageCollectedObject::value || - !std::is_trivially_destructible::type>::value; - - static void Finalize(void* obj) { - internal::FinalizerTraitImpl::Finalize(obj); - } - - public: - static constexpr bool HasFinalizer() { return kNonTrivialFinalizer; } - - // The callback used to finalize an object of type T. - static constexpr FinalizationCallback kCallback = - kNonTrivialFinalizer ? Finalize : nullptr; -}; - -template -constexpr FinalizationCallback FinalizerTrait::kCallback; - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/gc-info.h b/NativeScript/napi/v8/include/cppgc/internal/gc-info.h deleted file mode 100644 index c8cb99ac..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/gc-info.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ -#define INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ - -#include -#include -#include - -#include "cppgc/internal/finalizer-trait.h" -#include "cppgc/internal/logging.h" -#include "cppgc/internal/name-trait.h" -#include "cppgc/trace-trait.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -using GCInfoIndex = uint16_t; - -struct V8_EXPORT EnsureGCInfoIndexTrait final { - // Acquires a new GC info object and updates `registered_index` with the index - // that identifies that new info accordingly. - template - V8_INLINE static GCInfoIndex EnsureIndex( - std::atomic& registered_index) { - return EnsureGCInfoIndexTraitDispatch{}(registered_index); - } - - private: - template ::HasFinalizer(), - bool = NameTrait::HasNonHiddenName()> - struct EnsureGCInfoIndexTraitDispatch; - - static GCInfoIndex V8_PRESERVE_MOST - EnsureGCInfoIndex(std::atomic&, TraceCallback, - FinalizationCallback, NameCallback); - static GCInfoIndex V8_PRESERVE_MOST EnsureGCInfoIndex( - std::atomic&, TraceCallback, FinalizationCallback); - static GCInfoIndex V8_PRESERVE_MOST - EnsureGCInfoIndex(std::atomic&, TraceCallback, NameCallback); - static GCInfoIndex V8_PRESERVE_MOST - EnsureGCInfoIndex(std::atomic&, TraceCallback); -}; - -#define DISPATCH(has_finalizer, has_non_hidden_name, function) \ - template \ - struct EnsureGCInfoIndexTrait::EnsureGCInfoIndexTraitDispatch< \ - T, has_finalizer, has_non_hidden_name> { \ - V8_INLINE GCInfoIndex \ - operator()(std::atomic& registered_index) { \ - return function; \ - } \ - }; - -// ------------------------------------------------------- // -// DISPATCH(has_finalizer, has_non_hidden_name, function) // -// ------------------------------------------------------- // -DISPATCH(true, true, // - EnsureGCInfoIndex(registered_index, // - TraceTrait::Trace, // - FinalizerTrait::kCallback, // - NameTrait::GetName)) // -DISPATCH(true, false, // - EnsureGCInfoIndex(registered_index, // - TraceTrait::Trace, // - FinalizerTrait::kCallback)) // -DISPATCH(false, true, // - EnsureGCInfoIndex(registered_index, // - TraceTrait::Trace, // - NameTrait::GetName)) // -DISPATCH(false, false, // - EnsureGCInfoIndex(registered_index, // - TraceTrait::Trace)) // - -#undef DISPATCH - -// Trait determines how the garbage collector treats objects wrt. to traversing, -// finalization, and naming. -template -struct GCInfoTrait final { - V8_INLINE static GCInfoIndex Index() { - static_assert(sizeof(T), "T must be fully defined"); - static std::atomic - registered_index; // Uses zero initialization. - GCInfoIndex index = registered_index.load(std::memory_order_acquire); - if (V8_UNLIKELY(!index)) { - index = EnsureGCInfoIndexTrait::EnsureIndex(registered_index); - CPPGC_DCHECK(index != 0); - CPPGC_DCHECK(index == registered_index.load(std::memory_order_acquire)); - } - return index; - } - - static constexpr bool CheckCallbacksAreDefined() { - // No USE() macro available. - (void)static_cast(TraceTrait::Trace); - (void)static_cast(FinalizerTrait::kCallback); - (void)static_cast(NameTrait::GetName); - return true; - } -}; - -// Fold types based on finalizer behavior. Note that finalizer characteristics -// align with trace behavior, i.e., destructors are virtual when trace methods -// are and vice versa. -template -struct GCInfoFolding final { - static constexpr bool kHasVirtualDestructorAtBase = - std::has_virtual_destructor::value; - static constexpr bool kBothTypesAreTriviallyDestructible = - std::is_trivially_destructible::value && - std::is_trivially_destructible::value; - static constexpr bool kHasCustomFinalizerDispatchAtBase = - internal::HasFinalizeGarbageCollectedObject< - ParentMostGarbageCollectedType>::value; -#ifdef CPPGC_SUPPORTS_OBJECT_NAMES - static constexpr bool kWantsDetailedObjectNames = true; -#else // !CPPGC_SUPPORTS_OBJECT_NAMES - static constexpr bool kWantsDetailedObjectNames = false; -#endif // !CPPGC_SUPPORTS_OBJECT_NAMES - - // Always true. Forces the compiler to resolve callbacks which ensures that - // both modes don't break without requiring compiling a separate - // configuration. Only a single GCInfo (for `ResultType` below) will actually - // be instantiated but existence (and well-formedness) of all callbacks is - // checked. - static constexpr bool kCheckTypeGuardAlwaysTrue = - GCInfoTrait::CheckCallbacksAreDefined() && - GCInfoTrait::CheckCallbacksAreDefined(); - - // Folding would regress name resolution when deriving names from C++ - // class names as it would just folds a name to the base class name. - using ResultType = - std::conditional_t; -}; - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/logging.h b/NativeScript/napi/v8/include/cppgc/internal/logging.h deleted file mode 100644 index 3a279fe0..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/logging.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_LOGGING_H_ -#define INCLUDE_CPPGC_INTERNAL_LOGGING_H_ - -#include "cppgc/source-location.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -void V8_EXPORT DCheckImpl(const char*, - const SourceLocation& = SourceLocation::Current()); -[[noreturn]] void V8_EXPORT -FatalImpl(const char*, const SourceLocation& = SourceLocation::Current()); - -// Used to ignore -Wunused-variable. -template -struct EatParams {}; - -#if defined(DEBUG) -#define CPPGC_DCHECK_MSG(condition, message) \ - do { \ - if (V8_UNLIKELY(!(condition))) { \ - ::cppgc::internal::DCheckImpl(message); \ - } \ - } while (false) -#else // !defined(DEBUG) -#define CPPGC_DCHECK_MSG(condition, message) \ - (static_cast(::cppgc::internal::EatParams(condition), message)>{})) -#endif // !defined(DEBUG) - -#define CPPGC_DCHECK(condition) CPPGC_DCHECK_MSG(condition, #condition) - -#define CPPGC_CHECK_MSG(condition, message) \ - do { \ - if (V8_UNLIKELY(!(condition))) { \ - ::cppgc::internal::FatalImpl(message); \ - } \ - } while (false) - -#define CPPGC_CHECK(condition) CPPGC_CHECK_MSG(condition, #condition) - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_LOGGING_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/member-storage.h b/NativeScript/napi/v8/include/cppgc/internal/member-storage.h deleted file mode 100644 index 61b255ba..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/member-storage.h +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2022 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ -#define INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ - -#include -#include -#include - -#include "cppgc/internal/api-constants.h" -#include "cppgc/internal/logging.h" -#include "cppgc/sentinel-pointer.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -enum class WriteBarrierSlotType { - kCompressed, - kUncompressed, -}; - -#if defined(CPPGC_POINTER_COMPRESSION) - -#if defined(__clang__) -// Attribute const allows the compiler to assume that CageBaseGlobal::g_base_ -// doesn't change (e.g. across calls) and thereby avoid redundant loads. -#define CPPGC_CONST __attribute__((const)) -#define CPPGC_REQUIRE_CONSTANT_INIT \ - __attribute__((require_constant_initialization)) -#else // defined(__clang__) -#define CPPGC_CONST -#define CPPGC_REQUIRE_CONSTANT_INIT -#endif // defined(__clang__) - -class V8_EXPORT CageBaseGlobal final { - public: - V8_INLINE CPPGC_CONST static uintptr_t Get() { - CPPGC_DCHECK(IsBaseConsistent()); - return g_base_.base; - } - - V8_INLINE CPPGC_CONST static bool IsSet() { - CPPGC_DCHECK(IsBaseConsistent()); - return (g_base_.base & ~kLowerHalfWordMask) != 0; - } - - private: - // We keep the lower halfword as ones to speed up decompression. - static constexpr uintptr_t kLowerHalfWordMask = - (api_constants::kCagedHeapReservationAlignment - 1); - - static union alignas(api_constants::kCachelineSize) Base { - uintptr_t base; - char cache_line[api_constants::kCachelineSize]; - } g_base_ CPPGC_REQUIRE_CONSTANT_INIT; - - CageBaseGlobal() = delete; - - V8_INLINE static bool IsBaseConsistent() { - return kLowerHalfWordMask == (g_base_.base & kLowerHalfWordMask); - } - - friend class CageBaseGlobalUpdater; -}; - -#undef CPPGC_REQUIRE_CONSTANT_INIT -#undef CPPGC_CONST - -class V8_TRIVIAL_ABI CompressedPointer final { - public: - using IntegralType = uint32_t; - static constexpr auto kWriteBarrierSlotType = - WriteBarrierSlotType::kCompressed; - - V8_INLINE CompressedPointer() : value_(0u) {} - V8_INLINE explicit CompressedPointer(const void* ptr) - : value_(Compress(ptr)) {} - V8_INLINE explicit CompressedPointer(std::nullptr_t) : value_(0u) {} - V8_INLINE explicit CompressedPointer(SentinelPointer) - : value_(kCompressedSentinel) {} - - V8_INLINE const void* Load() const { return Decompress(value_); } - V8_INLINE const void* LoadAtomic() const { - return Decompress( - reinterpret_cast&>(value_).load( - std::memory_order_relaxed)); - } - - V8_INLINE void Store(const void* ptr) { value_ = Compress(ptr); } - V8_INLINE void StoreAtomic(const void* value) { - reinterpret_cast&>(value_).store( - Compress(value), std::memory_order_relaxed); - } - - V8_INLINE void Clear() { value_ = 0u; } - V8_INLINE bool IsCleared() const { return !value_; } - - V8_INLINE bool IsSentinel() const { return value_ == kCompressedSentinel; } - - V8_INLINE uint32_t GetAsInteger() const { return value_; } - - V8_INLINE friend bool operator==(CompressedPointer a, CompressedPointer b) { - return a.value_ == b.value_; - } - V8_INLINE friend bool operator!=(CompressedPointer a, CompressedPointer b) { - return a.value_ != b.value_; - } - V8_INLINE friend bool operator<(CompressedPointer a, CompressedPointer b) { - return a.value_ < b.value_; - } - V8_INLINE friend bool operator<=(CompressedPointer a, CompressedPointer b) { - return a.value_ <= b.value_; - } - V8_INLINE friend bool operator>(CompressedPointer a, CompressedPointer b) { - return a.value_ > b.value_; - } - V8_INLINE friend bool operator>=(CompressedPointer a, CompressedPointer b) { - return a.value_ >= b.value_; - } - - static V8_INLINE IntegralType Compress(const void* ptr) { - static_assert(SentinelPointer::kSentinelValue == - 1 << api_constants::kPointerCompressionShift, - "The compression scheme relies on the sentinel encoded as 1 " - "<< kPointerCompressionShift"); - static constexpr size_t kGigaCageMask = - ~(api_constants::kCagedHeapReservationAlignment - 1); - static constexpr size_t kPointerCompressionShiftMask = - (1 << api_constants::kPointerCompressionShift) - 1; - - CPPGC_DCHECK(CageBaseGlobal::IsSet()); - const uintptr_t base = CageBaseGlobal::Get(); - CPPGC_DCHECK(!ptr || ptr == kSentinelPointer || - (base & kGigaCageMask) == - (reinterpret_cast(ptr) & kGigaCageMask)); - CPPGC_DCHECK( - (reinterpret_cast(ptr) & kPointerCompressionShiftMask) == 0); - -#if defined(CPPGC_2GB_CAGE) - // Truncate the pointer. - auto compressed = - static_cast(reinterpret_cast(ptr)); -#else // !defined(CPPGC_2GB_CAGE) - const auto uptr = reinterpret_cast(ptr); - // Shift the pointer and truncate. - auto compressed = static_cast( - uptr >> api_constants::kPointerCompressionShift); -#endif // !defined(CPPGC_2GB_CAGE) - // Normal compressed pointers must have the MSB set. - CPPGC_DCHECK((!compressed || compressed == kCompressedSentinel) || - (compressed & (1 << 31))); - return compressed; - } - - static V8_INLINE void* Decompress(IntegralType ptr) { - CPPGC_DCHECK(CageBaseGlobal::IsSet()); - const uintptr_t base = CageBaseGlobal::Get(); - // Treat compressed pointer as signed and cast it to uint64_t, which will - // sign-extend it. -#if defined(CPPGC_2GB_CAGE) - const uint64_t mask = static_cast(static_cast(ptr)); -#else // !defined(CPPGC_2GB_CAGE) - // Then, shift the result. It's important to shift the unsigned - // value, as otherwise it would result in undefined behavior. - const uint64_t mask = static_cast(static_cast(ptr)) - << api_constants::kPointerCompressionShift; -#endif // !defined(CPPGC_2GB_CAGE) - return reinterpret_cast(mask & base); - } - - private: -#if defined(CPPGC_2GB_CAGE) - static constexpr IntegralType kCompressedSentinel = - SentinelPointer::kSentinelValue; -#else // !defined(CPPGC_2GB_CAGE) - static constexpr IntegralType kCompressedSentinel = - SentinelPointer::kSentinelValue >> - api_constants::kPointerCompressionShift; -#endif // !defined(CPPGC_2GB_CAGE) - // All constructors initialize `value_`. Do not add a default value here as it - // results in a non-atomic write on some builds, even when the atomic version - // of the constructor is used. - IntegralType value_; -}; - -#endif // defined(CPPGC_POINTER_COMPRESSION) - -class V8_TRIVIAL_ABI RawPointer final { - public: - using IntegralType = uintptr_t; - static constexpr auto kWriteBarrierSlotType = - WriteBarrierSlotType::kUncompressed; - - V8_INLINE RawPointer() : ptr_(nullptr) {} - V8_INLINE explicit RawPointer(const void* ptr) : ptr_(ptr) {} - - V8_INLINE const void* Load() const { return ptr_; } - V8_INLINE const void* LoadAtomic() const { - return reinterpret_cast&>(ptr_).load( - std::memory_order_relaxed); - } - - V8_INLINE void Store(const void* ptr) { ptr_ = ptr; } - V8_INLINE void StoreAtomic(const void* ptr) { - reinterpret_cast&>(ptr_).store( - ptr, std::memory_order_relaxed); - } - - V8_INLINE void Clear() { ptr_ = nullptr; } - V8_INLINE bool IsCleared() const { return !ptr_; } - - V8_INLINE bool IsSentinel() const { return ptr_ == kSentinelPointer; } - - V8_INLINE uintptr_t GetAsInteger() const { - return reinterpret_cast(ptr_); - } - - V8_INLINE friend bool operator==(RawPointer a, RawPointer b) { - return a.ptr_ == b.ptr_; - } - V8_INLINE friend bool operator!=(RawPointer a, RawPointer b) { - return a.ptr_ != b.ptr_; - } - V8_INLINE friend bool operator<(RawPointer a, RawPointer b) { - return a.ptr_ < b.ptr_; - } - V8_INLINE friend bool operator<=(RawPointer a, RawPointer b) { - return a.ptr_ <= b.ptr_; - } - V8_INLINE friend bool operator>(RawPointer a, RawPointer b) { - return a.ptr_ > b.ptr_; - } - V8_INLINE friend bool operator>=(RawPointer a, RawPointer b) { - return a.ptr_ >= b.ptr_; - } - - private: - // All constructors initialize `ptr_`. Do not add a default value here as it - // results in a non-atomic write on some builds, even when the atomic version - // of the constructor is used. - const void* ptr_; -}; - -#if defined(CPPGC_POINTER_COMPRESSION) -using DefaultMemberStorage = CompressedPointer; -#else // !defined(CPPGC_POINTER_COMPRESSION) -using DefaultMemberStorage = RawPointer; -#endif // !defined(CPPGC_POINTER_COMPRESSION) - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/name-trait.h b/NativeScript/napi/v8/include/cppgc/internal/name-trait.h deleted file mode 100644 index 1d927a9d..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/name-trait.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ -#define INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ - -#include -#include -#include - -#include "cppgc/name-provider.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -#if CPPGC_SUPPORTS_OBJECT_NAMES && defined(__clang__) -#define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 1 - -// Provides constexpr c-string storage for a name of fixed |Size| characters. -// Automatically appends terminating 0 byte. -template -struct NameBuffer { - char name[Size + 1]{}; - - static constexpr NameBuffer FromCString(const char* str) { - NameBuffer result; - for (size_t i = 0; i < Size; ++i) result.name[i] = str[i]; - result.name[Size] = 0; - return result; - } -}; - -template -const char* GetTypename() { - static constexpr char kSelfPrefix[] = - "const char *cppgc::internal::GetTypename() [T ="; - static_assert(__builtin_strncmp(__PRETTY_FUNCTION__, kSelfPrefix, - sizeof(kSelfPrefix) - 1) == 0, - "The prefix must match"); - static constexpr const char* kTypenameStart = - __PRETTY_FUNCTION__ + sizeof(kSelfPrefix); - static constexpr size_t kTypenameSize = - __builtin_strlen(__PRETTY_FUNCTION__) - sizeof(kSelfPrefix) - 1; - // NameBuffer is an indirection that is needed to make sure that only a - // substring of __PRETTY_FUNCTION__ gets materialized in the binary. - static constexpr auto buffer = - NameBuffer::FromCString(kTypenameStart); - return buffer.name; -} - -#else -#define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 0 -#endif - -struct HeapObjectName { - const char* value; - bool name_was_hidden; -}; - -enum class HeapObjectNameForUnnamedObject : uint8_t { - kUseClassNameIfSupported, - kUseHiddenName, -}; - -class V8_EXPORT NameTraitBase { - protected: - static HeapObjectName GetNameFromTypeSignature(const char*); -}; - -// Trait that specifies how the garbage collector retrieves the name for a -// given object. -template -class NameTrait final : public NameTraitBase { - public: - static constexpr bool HasNonHiddenName() { -#if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME - return true; -#elif CPPGC_SUPPORTS_OBJECT_NAMES - return true; -#else // !CPPGC_SUPPORTS_OBJECT_NAMES - return std::is_base_of::value; -#endif // !CPPGC_SUPPORTS_OBJECT_NAMES - } - - static HeapObjectName GetName( - const void* obj, HeapObjectNameForUnnamedObject name_retrieval_mode) { - return GetNameFor(static_cast(obj), name_retrieval_mode); - } - - private: - static HeapObjectName GetNameFor(const NameProvider* name_provider, - HeapObjectNameForUnnamedObject) { - // Objects inheriting from `NameProvider` are not considered unnamed as - // users already provided a name for them. - return {name_provider->GetHumanReadableName(), false}; - } - - static HeapObjectName GetNameFor( - const void*, HeapObjectNameForUnnamedObject name_retrieval_mode) { - if (name_retrieval_mode == HeapObjectNameForUnnamedObject::kUseHiddenName) - return {NameProvider::kHiddenName, true}; - -#if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME - return {GetTypename(), false}; -#elif CPPGC_SUPPORTS_OBJECT_NAMES - -#if defined(V8_CC_GNU) -#define PRETTY_FUNCTION_VALUE __PRETTY_FUNCTION__ -#elif defined(V8_CC_MSVC) -#define PRETTY_FUNCTION_VALUE __FUNCSIG__ -#else -#define PRETTY_FUNCTION_VALUE nullptr -#endif - - static const HeapObjectName leaky_name = - GetNameFromTypeSignature(PRETTY_FUNCTION_VALUE); - return leaky_name; - -#undef PRETTY_FUNCTION_VALUE - -#else // !CPPGC_SUPPORTS_OBJECT_NAMES - return {NameProvider::kHiddenName, true}; -#endif // !CPPGC_SUPPORTS_OBJECT_NAMES - } -}; - -using NameCallback = HeapObjectName (*)(const void*, - HeapObjectNameForUnnamedObject); - -} // namespace internal -} // namespace cppgc - -#undef CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME - -#endif // INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/persistent-node.h b/NativeScript/napi/v8/include/cppgc/internal/persistent-node.h deleted file mode 100644 index d22692a7..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/persistent-node.h +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ -#define INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ - -#include -#include -#include - -#include "cppgc/internal/logging.h" -#include "cppgc/trace-trait.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -class CrossThreadPersistentRegion; -class FatalOutOfMemoryHandler; -class RootVisitor; - -// PersistentNode represents a variant of two states: -// 1) traceable node with a back pointer to the Persistent object; -// 2) freelist entry. -class PersistentNode final { - public: - PersistentNode() = default; - - PersistentNode(const PersistentNode&) = delete; - PersistentNode& operator=(const PersistentNode&) = delete; - - void InitializeAsUsedNode(void* owner, TraceRootCallback trace) { - CPPGC_DCHECK(trace); - owner_ = owner; - trace_ = trace; - } - - void InitializeAsFreeNode(PersistentNode* next) { - next_ = next; - trace_ = nullptr; - } - - void UpdateOwner(void* owner) { - CPPGC_DCHECK(IsUsed()); - owner_ = owner; - } - - PersistentNode* FreeListNext() const { - CPPGC_DCHECK(!IsUsed()); - return next_; - } - - void Trace(RootVisitor& root_visitor) const { - CPPGC_DCHECK(IsUsed()); - trace_(root_visitor, owner_); - } - - bool IsUsed() const { return trace_; } - - void* owner() const { - CPPGC_DCHECK(IsUsed()); - return owner_; - } - - private: - // PersistentNode acts as a designated union: - // If trace_ != nullptr, owner_ points to the corresponding Persistent handle. - // Otherwise, next_ points to the next freed PersistentNode. - union { - void* owner_ = nullptr; - PersistentNode* next_; - }; - TraceRootCallback trace_ = nullptr; -}; - -class V8_EXPORT PersistentRegionBase { - using PersistentNodeSlots = std::array; - - public: - // Clears Persistent fields to avoid stale pointers after heap teardown. - ~PersistentRegionBase(); - - PersistentRegionBase(const PersistentRegionBase&) = delete; - PersistentRegionBase& operator=(const PersistentRegionBase&) = delete; - - void Iterate(RootVisitor&); - - size_t NodesInUse() const; - - void ClearAllUsedNodes(); - - protected: - explicit PersistentRegionBase(const FatalOutOfMemoryHandler& oom_handler); - - PersistentNode* TryAllocateNodeFromFreeList(void* owner, - TraceRootCallback trace) { - PersistentNode* node = nullptr; - if (V8_LIKELY(free_list_head_)) { - node = free_list_head_; - free_list_head_ = free_list_head_->FreeListNext(); - CPPGC_DCHECK(!node->IsUsed()); - node->InitializeAsUsedNode(owner, trace); - nodes_in_use_++; - } - return node; - } - - void FreeNode(PersistentNode* node) { - CPPGC_DCHECK(node); - CPPGC_DCHECK(node->IsUsed()); - node->InitializeAsFreeNode(free_list_head_); - free_list_head_ = node; - CPPGC_DCHECK(nodes_in_use_ > 0); - nodes_in_use_--; - } - - PersistentNode* RefillFreeListAndAllocateNode(void* owner, - TraceRootCallback trace); - - private: - template - void ClearAllUsedNodes(); - - void RefillFreeList(); - - std::vector> nodes_; - PersistentNode* free_list_head_ = nullptr; - size_t nodes_in_use_ = 0; - const FatalOutOfMemoryHandler& oom_handler_; - - friend class CrossThreadPersistentRegion; -}; - -// Variant of PersistentRegionBase that checks whether the allocation and -// freeing happens only on the thread that created the region. -class V8_EXPORT PersistentRegion final : public PersistentRegionBase { - public: - explicit PersistentRegion(const FatalOutOfMemoryHandler&); - // Clears Persistent fields to avoid stale pointers after heap teardown. - ~PersistentRegion() = default; - - PersistentRegion(const PersistentRegion&) = delete; - PersistentRegion& operator=(const PersistentRegion&) = delete; - - V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { - CPPGC_DCHECK(IsCreationThread()); - auto* node = TryAllocateNodeFromFreeList(owner, trace); - if (V8_LIKELY(node)) return node; - - // Slow path allocation allows for checking thread correspondence. - CPPGC_CHECK(IsCreationThread()); - return RefillFreeListAndAllocateNode(owner, trace); - } - - V8_INLINE void FreeNode(PersistentNode* node) { - CPPGC_DCHECK(IsCreationThread()); - PersistentRegionBase::FreeNode(node); - } - - private: - bool IsCreationThread(); - - int creation_thread_id_; -}; - -// CrossThreadPersistent uses PersistentRegionBase but protects it using this -// lock when needed. -class V8_EXPORT PersistentRegionLock final { - public: - PersistentRegionLock(); - ~PersistentRegionLock(); - - static void AssertLocked(); -}; - -// Variant of PersistentRegionBase that checks whether the PersistentRegionLock -// is locked. -class V8_EXPORT CrossThreadPersistentRegion final - : protected PersistentRegionBase { - public: - explicit CrossThreadPersistentRegion(const FatalOutOfMemoryHandler&); - // Clears Persistent fields to avoid stale pointers after heap teardown. - ~CrossThreadPersistentRegion(); - - CrossThreadPersistentRegion(const CrossThreadPersistentRegion&) = delete; - CrossThreadPersistentRegion& operator=(const CrossThreadPersistentRegion&) = - delete; - - V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { - PersistentRegionLock::AssertLocked(); - auto* node = TryAllocateNodeFromFreeList(owner, trace); - if (V8_LIKELY(node)) return node; - - return RefillFreeListAndAllocateNode(owner, trace); - } - - V8_INLINE void FreeNode(PersistentNode* node) { - PersistentRegionLock::AssertLocked(); - PersistentRegionBase::FreeNode(node); - } - - void Iterate(RootVisitor&); - - size_t NodesInUse() const; - - void ClearAllUsedNodes(); -}; - -} // namespace internal - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/pointer-policies.h b/NativeScript/napi/v8/include/cppgc/internal/pointer-policies.h deleted file mode 100644 index 06fa884f..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/pointer-policies.h +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ -#define INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ - -#include -#include - -#include "cppgc/internal/member-storage.h" -#include "cppgc/internal/write-barrier.h" -#include "cppgc/sentinel-pointer.h" -#include "cppgc/source-location.h" -#include "cppgc/type-traits.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -class HeapBase; -class PersistentRegion; -class CrossThreadPersistentRegion; - -// Tags to distinguish between strong and weak member types. -class StrongMemberTag; -class WeakMemberTag; -class UntracedMemberTag; - -struct DijkstraWriteBarrierPolicy { - V8_INLINE static void InitializingBarrier(const void*, const void*) { - // Since in initializing writes the source object is always white, having no - // barrier doesn't break the tri-color invariant. - } - - template - V8_INLINE static void AssigningBarrier(const void* slot, const void* value) { -#ifdef CPPGC_SLIM_WRITE_BARRIER - if (V8_UNLIKELY(WriteBarrier::IsEnabled())) - WriteBarrier::CombinedWriteBarrierSlow(slot); -#else // !CPPGC_SLIM_WRITE_BARRIER - WriteBarrier::Params params; - const WriteBarrier::Type type = - WriteBarrier::GetWriteBarrierType(slot, value, params); - WriteBarrier(type, params, slot, value); -#endif // !CPPGC_SLIM_WRITE_BARRIER - } - - template - V8_INLINE static void AssigningBarrier(const void* slot, RawPointer storage) { - static_assert( - SlotType == WriteBarrierSlotType::kUncompressed, - "Assigning storages of Member and UncompressedMember is not supported"); -#ifdef CPPGC_SLIM_WRITE_BARRIER - if (V8_UNLIKELY(WriteBarrier::IsEnabled())) - WriteBarrier::CombinedWriteBarrierSlow(slot); -#else // !CPPGC_SLIM_WRITE_BARRIER - WriteBarrier::Params params; - const WriteBarrier::Type type = - WriteBarrier::GetWriteBarrierType(slot, storage, params); - WriteBarrier(type, params, slot, storage.Load()); -#endif // !CPPGC_SLIM_WRITE_BARRIER - } - -#if defined(CPPGC_POINTER_COMPRESSION) - template - V8_INLINE static void AssigningBarrier(const void* slot, - CompressedPointer storage) { - static_assert( - SlotType == WriteBarrierSlotType::kCompressed, - "Assigning storages of Member and UncompressedMember is not supported"); -#ifdef CPPGC_SLIM_WRITE_BARRIER - if (V8_UNLIKELY(WriteBarrier::IsEnabled())) - WriteBarrier::CombinedWriteBarrierSlow(slot); -#else // !CPPGC_SLIM_WRITE_BARRIER - WriteBarrier::Params params; - const WriteBarrier::Type type = - WriteBarrier::GetWriteBarrierType(slot, storage, params); - WriteBarrier(type, params, slot, storage.Load()); -#endif // !CPPGC_SLIM_WRITE_BARRIER - } -#endif // defined(CPPGC_POINTER_COMPRESSION) - - private: - V8_INLINE static void WriteBarrier(WriteBarrier::Type type, - const WriteBarrier::Params& params, - const void* slot, const void* value) { - switch (type) { - case WriteBarrier::Type::kGenerational: - WriteBarrier::GenerationalBarrier< - WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot); - break; - case WriteBarrier::Type::kMarking: - WriteBarrier::DijkstraMarkingBarrier(params, value); - break; - case WriteBarrier::Type::kNone: - break; - } - } -}; - -struct NoWriteBarrierPolicy { - V8_INLINE static void InitializingBarrier(const void*, const void*) {} - template - V8_INLINE static void AssigningBarrier(const void*, const void*) {} - template - V8_INLINE static void AssigningBarrier(const void*, MemberStorage) {} -}; - -class V8_EXPORT SameThreadEnabledCheckingPolicyBase { - protected: - void CheckPointerImpl(const void* ptr, bool points_to_payload, - bool check_off_heap_assignments); - - const HeapBase* heap_ = nullptr; -}; - -template -class V8_EXPORT SameThreadEnabledCheckingPolicy - : private SameThreadEnabledCheckingPolicyBase { - protected: - template - void CheckPointer(const T* ptr) { - if (!ptr || (kSentinelPointer == ptr)) return; - - CheckPointersImplTrampoline::Call(this, ptr); - } - - private: - template > - struct CheckPointersImplTrampoline { - static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) { - policy->CheckPointerImpl(ptr, false, kCheckOffHeapAssignments); - } - }; - - template - struct CheckPointersImplTrampoline { - static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) { - policy->CheckPointerImpl(ptr, IsGarbageCollectedTypeV, - kCheckOffHeapAssignments); - } - }; -}; - -class DisabledCheckingPolicy { - protected: - V8_INLINE void CheckPointer(const void*) {} -}; - -#ifdef DEBUG -// Off heap members are not connected to object graph and thus cannot ressurect -// dead objects. -using DefaultMemberCheckingPolicy = - SameThreadEnabledCheckingPolicy; -using DefaultPersistentCheckingPolicy = - SameThreadEnabledCheckingPolicy; -#else // !DEBUG -using DefaultMemberCheckingPolicy = DisabledCheckingPolicy; -using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy; -#endif // !DEBUG -// For CT(W)P neither marking information (for value), nor objectstart bitmap -// (for slot) are guaranteed to be present because there's no synchronization -// between heaps after marking. -using DefaultCrossThreadPersistentCheckingPolicy = DisabledCheckingPolicy; - -class KeepLocationPolicy { - public: - constexpr const SourceLocation& Location() const { return location_; } - - protected: - constexpr KeepLocationPolicy() = default; - constexpr explicit KeepLocationPolicy(const SourceLocation& location) - : location_(location) {} - - // KeepLocationPolicy must not copy underlying source locations. - KeepLocationPolicy(const KeepLocationPolicy&) = delete; - KeepLocationPolicy& operator=(const KeepLocationPolicy&) = delete; - - // Location of the original moved from object should be preserved. - KeepLocationPolicy(KeepLocationPolicy&&) = default; - KeepLocationPolicy& operator=(KeepLocationPolicy&&) = default; - - private: - SourceLocation location_; -}; - -class IgnoreLocationPolicy { - public: - constexpr SourceLocation Location() const { return {}; } - - protected: - constexpr IgnoreLocationPolicy() = default; - constexpr explicit IgnoreLocationPolicy(const SourceLocation&) {} -}; - -#if CPPGC_SUPPORTS_OBJECT_NAMES -using DefaultLocationPolicy = KeepLocationPolicy; -#else -using DefaultLocationPolicy = IgnoreLocationPolicy; -#endif - -struct StrongPersistentPolicy { - using IsStrongPersistent = std::true_type; - static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); -}; - -struct WeakPersistentPolicy { - using IsStrongPersistent = std::false_type; - static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); -}; - -struct StrongCrossThreadPersistentPolicy { - using IsStrongPersistent = std::true_type; - static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion( - const void* object); -}; - -struct WeakCrossThreadPersistentPolicy { - using IsStrongPersistent = std::false_type; - static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion( - const void* object); -}; - -// Forward declarations setting up the default policies. -template -class BasicCrossThreadPersistent; -template -class BasicPersistent; -template -class BasicMember; - -} // namespace internal - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ diff --git a/NativeScript/napi/v8/include/cppgc/internal/write-barrier.h b/NativeScript/napi/v8/include/cppgc/internal/write-barrier.h deleted file mode 100644 index 566724d3..00000000 --- a/NativeScript/napi/v8/include/cppgc/internal/write-barrier.h +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ -#define INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ - -#include -#include - -#include "cppgc/heap-handle.h" -#include "cppgc/heap-state.h" -#include "cppgc/internal/api-constants.h" -#include "cppgc/internal/atomic-entry-flag.h" -#include "cppgc/internal/base-page-handle.h" -#include "cppgc/internal/member-storage.h" -#include "cppgc/platform.h" -#include "cppgc/sentinel-pointer.h" -#include "cppgc/trace-trait.h" -#include "v8config.h" // NOLINT(build/include_directory) - -#if defined(CPPGC_CAGED_HEAP) -#include "cppgc/internal/caged-heap-local-data.h" -#include "cppgc/internal/caged-heap.h" -#endif - -namespace cppgc { - -class HeapHandle; - -namespace internal { - -#if defined(CPPGC_CAGED_HEAP) -class WriteBarrierTypeForCagedHeapPolicy; -#else // !CPPGC_CAGED_HEAP -class WriteBarrierTypeForNonCagedHeapPolicy; -#endif // !CPPGC_CAGED_HEAP - -class V8_EXPORT WriteBarrier final { - public: - enum class Type : uint8_t { - kNone, - kMarking, - kGenerational, - }; - - enum class GenerationalBarrierType : uint8_t { - kPreciseSlot, - kPreciseUncompressedSlot, - kImpreciseSlot, - }; - - struct Params { - HeapHandle* heap = nullptr; -#if V8_ENABLE_CHECKS - Type type = Type::kNone; -#endif // !V8_ENABLE_CHECKS -#if defined(CPPGC_CAGED_HEAP) - uintptr_t slot_offset = 0; - uintptr_t value_offset = 0; -#endif // CPPGC_CAGED_HEAP - }; - - enum class ValueMode { - kValuePresent, - kNoValuePresent, - }; - - // Returns the required write barrier for a given `slot` and `value`. - static V8_INLINE Type GetWriteBarrierType(const void* slot, const void* value, - Params& params); - // Returns the required write barrier for a given `slot` and `value`. - template - static V8_INLINE Type GetWriteBarrierType(const void* slot, MemberStorage, - Params& params); - // Returns the required write barrier for a given `slot`. - template - static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params, - HeapHandleCallback callback); - // Returns the required write barrier for a given `value`. - static V8_INLINE Type GetWriteBarrierType(const void* value, Params& params); - -#ifdef CPPGC_SLIM_WRITE_BARRIER - // A write barrier that combines `GenerationalBarrier()` and - // `DijkstraMarkingBarrier()`. We only pass a single parameter here to clobber - // as few registers as possible. - template - static V8_NOINLINE void V8_PRESERVE_MOST - CombinedWriteBarrierSlow(const void* slot); -#endif // CPPGC_SLIM_WRITE_BARRIER - - static V8_INLINE void DijkstraMarkingBarrier(const Params& params, - const void* object); - static V8_INLINE void DijkstraMarkingBarrierRange( - const Params& params, const void* first_element, size_t element_size, - size_t number_of_elements, TraceCallback trace_callback); - static V8_INLINE void SteeleMarkingBarrier(const Params& params, - const void* object); -#if defined(CPPGC_YOUNG_GENERATION) - template - static V8_INLINE void GenerationalBarrier(const Params& params, - const void* slot); -#else // !CPPGC_YOUNG_GENERATION - template - static V8_INLINE void GenerationalBarrier(const Params& params, - const void* slot){} -#endif // CPPGC_YOUNG_GENERATION - -#if V8_ENABLE_CHECKS - static void CheckParams(Type expected_type, const Params& params); -#else // !V8_ENABLE_CHECKS - static void CheckParams(Type expected_type, const Params& params) {} -#endif // !V8_ENABLE_CHECKS - - // The FlagUpdater class allows cppgc internal to update - // |write_barrier_enabled_|. - class FlagUpdater; - static bool IsEnabled() { return write_barrier_enabled_.MightBeEntered(); } - - private: - WriteBarrier() = delete; - -#if defined(CPPGC_CAGED_HEAP) - using WriteBarrierTypePolicy = WriteBarrierTypeForCagedHeapPolicy; -#else // !CPPGC_CAGED_HEAP - using WriteBarrierTypePolicy = WriteBarrierTypeForNonCagedHeapPolicy; -#endif // !CPPGC_CAGED_HEAP - - static void DijkstraMarkingBarrierSlow(const void* value); - static void DijkstraMarkingBarrierSlowWithSentinelCheck(const void* value); - static void DijkstraMarkingBarrierRangeSlow(HeapHandle& heap_handle, - const void* first_element, - size_t element_size, - size_t number_of_elements, - TraceCallback trace_callback); - static void SteeleMarkingBarrierSlow(const void* value); - static void SteeleMarkingBarrierSlowWithSentinelCheck(const void* value); - -#if defined(CPPGC_YOUNG_GENERATION) - static CagedHeapLocalData& GetLocalData(HeapHandle&); - static void GenerationalBarrierSlow(const CagedHeapLocalData& local_data, - const AgeTable& age_table, - const void* slot, uintptr_t value_offset, - HeapHandle* heap_handle); - static void GenerationalBarrierForUncompressedSlotSlow( - const CagedHeapLocalData& local_data, const AgeTable& age_table, - const void* slot, uintptr_t value_offset, HeapHandle* heap_handle); - static void GenerationalBarrierForSourceObjectSlow( - const CagedHeapLocalData& local_data, const void* object, - HeapHandle* heap_handle); -#endif // CPPGC_YOUNG_GENERATION - - static AtomicEntryFlag write_barrier_enabled_; -}; - -template -V8_INLINE WriteBarrier::Type SetAndReturnType(WriteBarrier::Params& params) { - if constexpr (type == WriteBarrier::Type::kNone) - return WriteBarrier::Type::kNone; -#if V8_ENABLE_CHECKS - params.type = type; -#endif // !V8_ENABLE_CHECKS - return type; -} - -#if defined(CPPGC_CAGED_HEAP) -class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { - public: - template - static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - return ValueModeDispatch::Get(slot, value, params, callback); - } - - template - static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - return ValueModeDispatch::Get(slot, value, params, callback); - } - - template - static V8_INLINE WriteBarrier::Type Get(const void* value, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - return GetNoSlot(value, params, callback); - } - - private: - WriteBarrierTypeForCagedHeapPolicy() = delete; - - template - static V8_INLINE WriteBarrier::Type GetNoSlot(const void* value, - WriteBarrier::Params& params, - HeapHandleCallback) { - const bool within_cage = CagedHeapBase::IsWithinCage(value); - if (!within_cage) return WriteBarrier::Type::kNone; - - // We know that |value| points either within the normal page or to the - // beginning of large-page, so extract the page header by bitmasking. - BasePageHandle* page = - BasePageHandle::FromPayload(const_cast(value)); - - HeapHandle& heap_handle = page->heap_handle(); - if (V8_UNLIKELY(heap_handle.is_incremental_marking_in_progress())) { - return SetAndReturnType(params); - } - - return SetAndReturnType(params); - } - - template - struct ValueModeDispatch; -}; - -template <> -struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< - WriteBarrier::ValueMode::kValuePresent> { - template - static V8_INLINE WriteBarrier::Type Get(const void* slot, - MemberStorage storage, - WriteBarrier::Params& params, - HeapHandleCallback) { - if (V8_LIKELY(!WriteBarrier::IsEnabled())) - return SetAndReturnType(params); - - return BarrierEnabledGet(slot, storage.Load(), params); - } - - template - static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, - WriteBarrier::Params& params, - HeapHandleCallback) { - if (V8_LIKELY(!WriteBarrier::IsEnabled())) - return SetAndReturnType(params); - - return BarrierEnabledGet(slot, value, params); - } - - private: - static V8_INLINE WriteBarrier::Type BarrierEnabledGet( - const void* slot, const void* value, WriteBarrier::Params& params) { - const bool within_cage = CagedHeapBase::AreWithinCage(slot, value); - if (!within_cage) return WriteBarrier::Type::kNone; - - // We know that |value| points either within the normal page or to the - // beginning of large-page, so extract the page header by bitmasking. - BasePageHandle* page = - BasePageHandle::FromPayload(const_cast(value)); - - HeapHandle& heap_handle = page->heap_handle(); - if (V8_LIKELY(!heap_handle.is_incremental_marking_in_progress())) { -#if defined(CPPGC_YOUNG_GENERATION) - if (!heap_handle.is_young_generation_enabled()) - return WriteBarrier::Type::kNone; - params.heap = &heap_handle; - params.slot_offset = CagedHeapBase::OffsetFromAddress(slot); - params.value_offset = CagedHeapBase::OffsetFromAddress(value); - return SetAndReturnType(params); -#else // !CPPGC_YOUNG_GENERATION - return SetAndReturnType(params); -#endif // !CPPGC_YOUNG_GENERATION - } - - // Use marking barrier. - params.heap = &heap_handle; - return SetAndReturnType(params); - } -}; - -template <> -struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< - WriteBarrier::ValueMode::kNoValuePresent> { - template - static V8_INLINE WriteBarrier::Type Get(const void* slot, const void*, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - if (V8_LIKELY(!WriteBarrier::IsEnabled())) - return SetAndReturnType(params); - - HeapHandle& handle = callback(); -#if defined(CPPGC_YOUNG_GENERATION) - if (V8_LIKELY(!handle.is_incremental_marking_in_progress())) { - if (!handle.is_young_generation_enabled()) { - return WriteBarrier::Type::kNone; - } - params.heap = &handle; - // Check if slot is on stack. - if (V8_UNLIKELY(!CagedHeapBase::IsWithinCage(slot))) { - return SetAndReturnType(params); - } - params.slot_offset = CagedHeapBase::OffsetFromAddress(slot); - return SetAndReturnType(params); - } -#else // !defined(CPPGC_YOUNG_GENERATION) - if (V8_UNLIKELY(!handle.is_incremental_marking_in_progress())) { - return SetAndReturnType(params); - } -#endif // !defined(CPPGC_YOUNG_GENERATION) - params.heap = &handle; - return SetAndReturnType(params); - } -}; - -#endif // CPPGC_CAGED_HEAP - -class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final { - public: - template - static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - return ValueModeDispatch::Get(slot, value, params, callback); - } - - template - static V8_INLINE WriteBarrier::Type Get(const void* slot, RawPointer value, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - return ValueModeDispatch::Get(slot, value.Load(), params, - callback); - } - - template - static V8_INLINE WriteBarrier::Type Get(const void* value, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - // The slot will never be used in `Get()` below. - return Get(nullptr, value, params, - callback); - } - - private: - template - struct ValueModeDispatch; - - WriteBarrierTypeForNonCagedHeapPolicy() = delete; -}; - -template <> -struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch< - WriteBarrier::ValueMode::kValuePresent> { - template - static V8_INLINE WriteBarrier::Type Get(const void*, const void* object, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - // The following check covers nullptr as well as sentinel pointer. - if (object <= static_cast(kSentinelPointer)) { - return SetAndReturnType(params); - } - if (V8_LIKELY(!WriteBarrier::IsEnabled())) { - return SetAndReturnType(params); - } - // We know that |object| is within the normal page or in the beginning of a - // large page, so extract the page header by bitmasking. - BasePageHandle* page = - BasePageHandle::FromPayload(const_cast(object)); - - HeapHandle& heap_handle = page->heap_handle(); - if (V8_LIKELY(heap_handle.is_incremental_marking_in_progress())) { - return SetAndReturnType(params); - } - return SetAndReturnType(params); - } -}; - -template <> -struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch< - WriteBarrier::ValueMode::kNoValuePresent> { - template - static V8_INLINE WriteBarrier::Type Get(const void*, const void*, - WriteBarrier::Params& params, - HeapHandleCallback callback) { - if (V8_UNLIKELY(WriteBarrier::IsEnabled())) { - HeapHandle& handle = callback(); - if (V8_LIKELY(handle.is_incremental_marking_in_progress())) { - params.heap = &handle; - return SetAndReturnType(params); - } - } - return WriteBarrier::Type::kNone; - } -}; - -// static -WriteBarrier::Type WriteBarrier::GetWriteBarrierType( - const void* slot, const void* value, WriteBarrier::Params& params) { - return WriteBarrierTypePolicy::Get(slot, value, - params, []() {}); -} - -// static -template -WriteBarrier::Type WriteBarrier::GetWriteBarrierType( - const void* slot, MemberStorage value, WriteBarrier::Params& params) { - return WriteBarrierTypePolicy::Get(slot, value, - params, []() {}); -} - -// static -template -WriteBarrier::Type WriteBarrier::GetWriteBarrierType( - const void* slot, WriteBarrier::Params& params, - HeapHandleCallback callback) { - return WriteBarrierTypePolicy::Get( - slot, nullptr, params, callback); -} - -// static -WriteBarrier::Type WriteBarrier::GetWriteBarrierType( - const void* value, WriteBarrier::Params& params) { - return WriteBarrierTypePolicy::Get(value, params, - []() {}); -} - -// static -void WriteBarrier::DijkstraMarkingBarrier(const Params& params, - const void* object) { - CheckParams(Type::kMarking, params); -#if defined(CPPGC_CAGED_HEAP) - // Caged heap already filters out sentinels. - DijkstraMarkingBarrierSlow(object); -#else // !CPPGC_CAGED_HEAP - DijkstraMarkingBarrierSlowWithSentinelCheck(object); -#endif // !CPPGC_CAGED_HEAP -} - -// static -void WriteBarrier::DijkstraMarkingBarrierRange(const Params& params, - const void* first_element, - size_t element_size, - size_t number_of_elements, - TraceCallback trace_callback) { - CheckParams(Type::kMarking, params); - DijkstraMarkingBarrierRangeSlow(*params.heap, first_element, element_size, - number_of_elements, trace_callback); -} - -// static -void WriteBarrier::SteeleMarkingBarrier(const Params& params, - const void* object) { - CheckParams(Type::kMarking, params); -#if defined(CPPGC_CAGED_HEAP) - // Caged heap already filters out sentinels. - SteeleMarkingBarrierSlow(object); -#else // !CPPGC_CAGED_HEAP - SteeleMarkingBarrierSlowWithSentinelCheck(object); -#endif // !CPPGC_CAGED_HEAP -} - -#if defined(CPPGC_YOUNG_GENERATION) - -// static -template -void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) { - CheckParams(Type::kGenerational, params); - - const CagedHeapLocalData& local_data = CagedHeapLocalData::Get(); - const AgeTable& age_table = local_data.age_table; - - // Bail out if the slot (precise or imprecise) is in young generation. - if (V8_LIKELY(age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung)) - return; - - // Dispatch between different types of barriers. - // TODO(chromium:1029379): Consider reload local_data in the slow path to - // reduce register pressure. - if constexpr (type == GenerationalBarrierType::kPreciseSlot) { - GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset, - params.heap); - } else if constexpr (type == - GenerationalBarrierType::kPreciseUncompressedSlot) { - GenerationalBarrierForUncompressedSlotSlow( - local_data, age_table, slot, params.value_offset, params.heap); - } else { - GenerationalBarrierForSourceObjectSlow(local_data, slot, params.heap); - } -} - -#endif // !CPPGC_YOUNG_GENERATION - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ diff --git a/NativeScript/napi/v8/include/cppgc/liveness-broker.h b/NativeScript/napi/v8/include/cppgc/liveness-broker.h deleted file mode 100644 index 2c94f1c0..00000000 --- a/NativeScript/napi/v8/include/cppgc/liveness-broker.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_LIVENESS_BROKER_H_ -#define INCLUDE_CPPGC_LIVENESS_BROKER_H_ - -#include "cppgc/heap.h" -#include "cppgc/member.h" -#include "cppgc/sentinel-pointer.h" -#include "cppgc/trace-trait.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -namespace internal { -class LivenessBrokerFactory; -} // namespace internal - -/** - * The broker is passed to weak callbacks to allow (temporarily) querying - * the liveness state of an object. References to non-live objects must be - * cleared when `IsHeapObjectAlive()` returns false. - * - * \code - * class GCedWithCustomWeakCallback final - * : public GarbageCollected { - * public: - * UntracedMember bar; - * - * void CustomWeakCallbackMethod(const LivenessBroker& broker) { - * if (!broker.IsHeapObjectAlive(bar)) - * bar = nullptr; - * } - * - * void Trace(cppgc::Visitor* visitor) const { - * visitor->RegisterWeakCallbackMethod< - * GCedWithCustomWeakCallback, - * &GCedWithCustomWeakCallback::CustomWeakCallbackMethod>(this); - * } - * }; - * \endcode - */ -class V8_EXPORT LivenessBroker final { - public: - template - bool IsHeapObjectAlive(const T* object) const { - // - nullptr objects are considered alive to allow weakness to be used from - // stack while running into a conservative GC. Treating nullptr as dead - // would mean that e.g. custom collections could not be strongified on - // stack. - // - Sentinel pointers are also preserved in weakness and not cleared. - return !object || object == kSentinelPointer || - IsHeapObjectAliveImpl( - TraceTrait::GetTraceDescriptor(object).base_object_payload); - } - - template - bool IsHeapObjectAlive(const WeakMember& weak_member) const { - return IsHeapObjectAlive(weak_member.Get()); - } - - template - bool IsHeapObjectAlive(const UntracedMember& untraced_member) const { - return IsHeapObjectAlive(untraced_member.Get()); - } - - private: - LivenessBroker() = default; - - bool IsHeapObjectAliveImpl(const void*) const; - - friend class internal::LivenessBrokerFactory; -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_LIVENESS_BROKER_H_ diff --git a/NativeScript/napi/v8/include/cppgc/macros.h b/NativeScript/napi/v8/include/cppgc/macros.h deleted file mode 100644 index a9ac22d7..00000000 --- a/NativeScript/napi/v8/include/cppgc/macros.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_MACROS_H_ -#define INCLUDE_CPPGC_MACROS_H_ - -#include - -#include "cppgc/internal/compiler-specific.h" - -namespace cppgc { - -// Use CPPGC_STACK_ALLOCATED if the object is only stack allocated. -// Add the CPPGC_STACK_ALLOCATED_IGNORE annotation on a case-by-case basis when -// enforcement of CPPGC_STACK_ALLOCATED should be suppressed. -#if defined(__clang__) -#define CPPGC_STACK_ALLOCATED() \ - public: \ - using IsStackAllocatedTypeMarker CPPGC_UNUSED = int; \ - \ - private: \ - void* operator new(size_t) = delete; \ - void* operator new(size_t, void*) = delete; \ - static_assert(true, "Force semicolon.") -#define CPPGC_STACK_ALLOCATED_IGNORE(bug_or_reason) \ - __attribute__((annotate("stack_allocated_ignore"))) -#else // !defined(__clang__) -#define CPPGC_STACK_ALLOCATED() static_assert(true, "Force semicolon.") -#define CPPGC_STACK_ALLOCATED_IGNORE(bug_or_reason) -#endif // !defined(__clang__) - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_MACROS_H_ diff --git a/NativeScript/napi/v8/include/cppgc/member.h b/NativeScript/napi/v8/include/cppgc/member.h deleted file mode 100644 index 457f163b..00000000 --- a/NativeScript/napi/v8/include/cppgc/member.h +++ /dev/null @@ -1,629 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_MEMBER_H_ -#define INCLUDE_CPPGC_MEMBER_H_ - -#include -#include -#include - -#include "cppgc/internal/api-constants.h" -#include "cppgc/internal/member-storage.h" -#include "cppgc/internal/pointer-policies.h" -#include "cppgc/sentinel-pointer.h" -#include "cppgc/type-traits.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -namespace subtle { -class HeapConsistency; -} // namespace subtle - -class Visitor; - -namespace internal { - -// MemberBase always refers to the object as const object and defers to -// BasicMember on casting to the right type as needed. -template -class V8_TRIVIAL_ABI MemberBase { - public: - using RawStorage = StorageType; - - protected: - struct AtomicInitializerTag {}; - - V8_INLINE MemberBase() = default; - V8_INLINE explicit MemberBase(const void* value) : raw_(value) {} - V8_INLINE MemberBase(const void* value, AtomicInitializerTag) { - SetRawAtomic(value); - } - - V8_INLINE explicit MemberBase(RawStorage raw) : raw_(raw) {} - V8_INLINE explicit MemberBase(std::nullptr_t) : raw_(nullptr) {} - V8_INLINE explicit MemberBase(SentinelPointer s) : raw_(s) {} - - V8_INLINE const void** GetRawSlot() const { - return reinterpret_cast(const_cast(this)); - } - V8_INLINE const void* GetRaw() const { return raw_.Load(); } - V8_INLINE void SetRaw(void* value) { raw_.Store(value); } - - V8_INLINE const void* GetRawAtomic() const { return raw_.LoadAtomic(); } - V8_INLINE void SetRawAtomic(const void* value) { raw_.StoreAtomic(value); } - - V8_INLINE RawStorage GetRawStorage() const { return raw_; } - V8_INLINE void SetRawStorageAtomic(RawStorage other) { - reinterpret_cast&>(raw_).store( - other, std::memory_order_relaxed); - } - - V8_INLINE bool IsCleared() const { return raw_.IsCleared(); } - - V8_INLINE void ClearFromGC() const { raw_.Clear(); } - - private: - friend class MemberDebugHelper; - - mutable RawStorage raw_; -}; - -// The basic class from which all Member classes are 'generated'. -template -class V8_TRIVIAL_ABI BasicMember final : private MemberBase, - private CheckingPolicy { - using Base = MemberBase; - - public: - using PointeeType = T; - using RawStorage = typename Base::RawStorage; - - V8_INLINE constexpr BasicMember() = default; - V8_INLINE constexpr BasicMember(std::nullptr_t) {} // NOLINT - V8_INLINE BasicMember(SentinelPointer s) : Base(s) {} // NOLINT - V8_INLINE BasicMember(T* raw) : Base(raw) { // NOLINT - InitializingWriteBarrier(raw); - this->CheckPointer(Get()); - } - V8_INLINE BasicMember(T& raw) // NOLINT - : BasicMember(&raw) {} - - // Atomic ctor. Using the AtomicInitializerTag forces BasicMember to - // initialize using atomic assignments. This is required for preventing - // data races with concurrent marking. - using AtomicInitializerTag = typename Base::AtomicInitializerTag; - V8_INLINE BasicMember(std::nullptr_t, AtomicInitializerTag atomic) - : Base(nullptr, atomic) {} - V8_INLINE BasicMember(SentinelPointer s, AtomicInitializerTag atomic) - : Base(s, atomic) {} - V8_INLINE BasicMember(T* raw, AtomicInitializerTag atomic) - : Base(raw, atomic) { - InitializingWriteBarrier(raw); - this->CheckPointer(Get()); - } - V8_INLINE BasicMember(T& raw, AtomicInitializerTag atomic) - : BasicMember(&raw, atomic) {} - - // Copy ctor. - V8_INLINE BasicMember(const BasicMember& other) - : BasicMember(other.GetRawStorage()) {} - - // Heterogeneous copy constructors. When the source pointer have a different - // type, perform a compress-decompress round, because the source pointer may - // need to be adjusted. - template >* = nullptr> - V8_INLINE BasicMember( // NOLINT - const BasicMember& other) - : BasicMember(other.GetRawStorage()) {} - - template >* = nullptr> - V8_INLINE BasicMember( // NOLINT - const BasicMember& other) - : BasicMember(other.Get()) {} - - // Move ctor. - V8_INLINE BasicMember(BasicMember&& other) noexcept - : BasicMember(other.GetRawStorage()) { - other.Clear(); - } - - // Heterogeneous move constructors. When the source pointer have a different - // type, perform a compress-decompress round, because the source pointer may - // need to be adjusted. - template >* = nullptr> - V8_INLINE BasicMember( - BasicMember&& other) noexcept - : BasicMember(other.GetRawStorage()) { - other.Clear(); - } - - template >* = nullptr> - V8_INLINE BasicMember( - BasicMember&& other) noexcept - : BasicMember(other.Get()) { - other.Clear(); - } - - // Construction from Persistent. - template ::value>> - V8_INLINE BasicMember(const BasicPersistent& p) - : BasicMember(p.Get()) {} - - // Copy assignment. - V8_INLINE BasicMember& operator=(const BasicMember& other) { - return operator=(other.GetRawStorage()); - } - - // Heterogeneous copy assignment. When the source pointer have a different - // type, perform a compress-decompress round, because the source pointer may - // need to be adjusted. - template - V8_INLINE BasicMember& operator=( - const BasicMember& other) { - if constexpr (internal::IsDecayedSameV) { - return operator=(other.GetRawStorage()); - } else { - static_assert(internal::IsStrictlyBaseOfV); - return operator=(other.Get()); - } - } - - // Move assignment. - V8_INLINE BasicMember& operator=(BasicMember&& other) noexcept { - operator=(other.GetRawStorage()); - other.Clear(); - return *this; - } - - // Heterogeneous move assignment. When the source pointer have a different - // type, perform a compress-decompress round, because the source pointer may - // need to be adjusted. - template - V8_INLINE BasicMember& operator=( - BasicMember&& other) noexcept { - if constexpr (internal::IsDecayedSameV) { - operator=(other.GetRawStorage()); - } else { - static_assert(internal::IsStrictlyBaseOfV); - operator=(other.Get()); - } - other.Clear(); - return *this; - } - - // Assignment from Persistent. - template ::value>> - V8_INLINE BasicMember& operator=( - const BasicPersistent& - other) { - return operator=(other.Get()); - } - - V8_INLINE BasicMember& operator=(T* other) { - Base::SetRawAtomic(other); - AssigningWriteBarrier(other); - this->CheckPointer(Get()); - return *this; - } - - V8_INLINE BasicMember& operator=(std::nullptr_t) { - Clear(); - return *this; - } - V8_INLINE BasicMember& operator=(SentinelPointer s) { - Base::SetRawAtomic(s); - return *this; - } - - template - V8_INLINE void Swap(BasicMember& other) { - auto tmp = GetRawStorage(); - *this = other; - other = tmp; - } - - V8_INLINE explicit operator bool() const { return !Base::IsCleared(); } - V8_INLINE operator T*() const { return Get(); } - V8_INLINE T* operator->() const { return Get(); } - V8_INLINE T& operator*() const { return *Get(); } - - // CFI cast exemption to allow passing SentinelPointer through T* and support - // heterogeneous assignments between different Member and Persistent handles - // based on their actual types. - V8_INLINE V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { - // Executed by the mutator, hence non atomic load. - // - // The const_cast below removes the constness from MemberBase storage. The - // following static_cast re-adds any constness if specified through the - // user-visible template parameter T. - return static_cast(const_cast(Base::GetRaw())); - } - - V8_INLINE void Clear() { - Base::SetRawStorageAtomic(RawStorage{}); - } - - V8_INLINE T* Release() { - T* result = Get(); - Clear(); - return result; - } - - V8_INLINE const T** GetSlotForTesting() const { - return reinterpret_cast(Base::GetRawSlot()); - } - - V8_INLINE RawStorage GetRawStorage() const { - return Base::GetRawStorage(); - } - - private: - V8_INLINE explicit BasicMember(RawStorage raw) : Base(raw) { - InitializingWriteBarrier(Get()); - this->CheckPointer(Get()); - } - - V8_INLINE BasicMember& operator=(RawStorage other) { - Base::SetRawStorageAtomic(other); - AssigningWriteBarrier(); - this->CheckPointer(Get()); - return *this; - } - - V8_INLINE const T* GetRawAtomic() const { - return static_cast(Base::GetRawAtomic()); - } - - V8_INLINE void InitializingWriteBarrier(T* value) const { - WriteBarrierPolicy::InitializingBarrier(Base::GetRawSlot(), value); - } - V8_INLINE void AssigningWriteBarrier(T* value) const { - WriteBarrierPolicy::template AssigningBarrier< - StorageType::kWriteBarrierSlotType>(Base::GetRawSlot(), value); - } - V8_INLINE void AssigningWriteBarrier() const { - WriteBarrierPolicy::template AssigningBarrier< - StorageType::kWriteBarrierSlotType>(Base::GetRawSlot(), - Base::GetRawStorage()); - } - - V8_INLINE void ClearFromGC() const { Base::ClearFromGC(); } - - V8_INLINE T* GetFromGC() const { return Get(); } - - friend class cppgc::subtle::HeapConsistency; - friend class cppgc::Visitor; - template - friend struct cppgc::TraceTrait; - template - friend class BasicMember; -}; - -// Member equality operators. -template -V8_INLINE bool operator==( - const BasicMember& member1, - const BasicMember& member2) { - if constexpr (internal::IsDecayedSameV) { - // Check compressed pointers if types are the same. - return member1.GetRawStorage() == member2.GetRawStorage(); - } else { - static_assert(internal::IsStrictlyBaseOfV || - internal::IsStrictlyBaseOfV); - // Otherwise, check decompressed pointers. - return member1.Get() == member2.Get(); - } -} - -template -V8_INLINE bool operator!=( - const BasicMember& member1, - const BasicMember& member2) { - return !(member1 == member2); -} - -// Equality with raw pointers. -template -V8_INLINE bool operator==( - const BasicMember& member, - U* raw) { - // Never allow comparison with erased pointers. - static_assert(!internal::IsDecayedSameV); - - if constexpr (internal::IsDecayedSameV) { - // Check compressed pointers if types are the same. - return member.GetRawStorage() == StorageType(raw); - } else if constexpr (internal::IsStrictlyBaseOfV) { - // Cast the raw pointer to T, which may adjust the pointer. - return member.GetRawStorage() == StorageType(static_cast(raw)); - } else { - // Otherwise, decompressed the member. - return member.Get() == raw; - } -} - -template -V8_INLINE bool operator!=( - const BasicMember& member, - U* raw) { - return !(member == raw); -} - -template -V8_INLINE bool operator==( - T* raw, const BasicMember& member) { - return member == raw; -} - -template -V8_INLINE bool operator!=( - T* raw, const BasicMember& member) { - return !(raw == member); -} - -// Equality with sentinel. -template -V8_INLINE bool operator==( - const BasicMember& member, - SentinelPointer) { - return member.GetRawStorage().IsSentinel(); -} - -template -V8_INLINE bool operator!=( - const BasicMember& member, - SentinelPointer s) { - return !(member == s); -} - -template -V8_INLINE bool operator==( - SentinelPointer s, const BasicMember& member) { - return member == s; -} - -template -V8_INLINE bool operator!=( - SentinelPointer s, const BasicMember& member) { - return !(s == member); -} - -// Equality with nullptr. -template -V8_INLINE bool operator==( - const BasicMember& member, - std::nullptr_t) { - return !static_cast(member); -} - -template -V8_INLINE bool operator!=( - const BasicMember& member, - std::nullptr_t n) { - return !(member == n); -} - -template -V8_INLINE bool operator==( - std::nullptr_t n, const BasicMember& member) { - return member == n; -} - -template -V8_INLINE bool operator!=( - std::nullptr_t n, const BasicMember& member) { - return !(n == member); -} - -// Relational operators. -template -V8_INLINE bool operator<( - const BasicMember& member1, - const BasicMember& member2) { - static_assert( - internal::IsDecayedSameV, - "Comparison works only for same pointer type modulo cv-qualifiers"); - return member1.GetRawStorage() < member2.GetRawStorage(); -} - -template -V8_INLINE bool operator<=( - const BasicMember& member1, - const BasicMember& member2) { - static_assert( - internal::IsDecayedSameV, - "Comparison works only for same pointer type modulo cv-qualifiers"); - return member1.GetRawStorage() <= member2.GetRawStorage(); -} - -template -V8_INLINE bool operator>( - const BasicMember& member1, - const BasicMember& member2) { - static_assert( - internal::IsDecayedSameV, - "Comparison works only for same pointer type modulo cv-qualifiers"); - return member1.GetRawStorage() > member2.GetRawStorage(); -} - -template -V8_INLINE bool operator>=( - const BasicMember& member1, - const BasicMember& member2) { - static_assert( - internal::IsDecayedSameV, - "Comparison works only for same pointer type modulo cv-qualifiers"); - return member1.GetRawStorage() >= member2.GetRawStorage(); -} - -template -struct IsWeak> - : std::true_type {}; - -} // namespace internal - -/** - * Members are used in classes to contain strong pointers to other garbage - * collected objects. All Member fields of a class must be traced in the class' - * trace method. - */ -template -using Member = internal::BasicMember< - T, internal::StrongMemberTag, internal::DijkstraWriteBarrierPolicy, - internal::DefaultMemberCheckingPolicy, internal::DefaultMemberStorage>; - -/** - * WeakMember is similar to Member in that it is used to point to other garbage - * collected objects. However instead of creating a strong pointer to the - * object, the WeakMember creates a weak pointer, which does not keep the - * pointee alive. Hence if all pointers to to a heap allocated object are weak - * the object will be garbage collected. At the time of GC the weak pointers - * will automatically be set to null. - */ -template -using WeakMember = internal::BasicMember< - T, internal::WeakMemberTag, internal::DijkstraWriteBarrierPolicy, - internal::DefaultMemberCheckingPolicy, internal::DefaultMemberStorage>; - -/** - * UntracedMember is a pointer to an on-heap object that is not traced for some - * reason. Do not use this unless you know what you are doing. Keeping raw - * pointers to on-heap objects is prohibited unless used from stack. Pointee - * must be kept alive through other means. - */ -template -using UntracedMember = internal::BasicMember< - T, internal::UntracedMemberTag, internal::NoWriteBarrierPolicy, - internal::DefaultMemberCheckingPolicy, internal::DefaultMemberStorage>; - -namespace subtle { - -/** - * UncompressedMember. Use with care in hot paths that would otherwise cause - * many decompression cycles. - */ -template -using UncompressedMember = internal::BasicMember< - T, internal::StrongMemberTag, internal::DijkstraWriteBarrierPolicy, - internal::DefaultMemberCheckingPolicy, internal::RawPointer>; - -#if defined(CPPGC_POINTER_COMPRESSION) -/** - * CompressedMember. Default implementation of cppgc::Member on builds with - * pointer compression. - */ -template -using CompressedMember = internal::BasicMember< - T, internal::StrongMemberTag, internal::DijkstraWriteBarrierPolicy, - internal::DefaultMemberCheckingPolicy, internal::CompressedPointer>; -#endif // defined(CPPGC_POINTER_COMPRESSION) - -} // namespace subtle - -namespace internal { - -struct Dummy; - -static constexpr size_t kSizeOfMember = sizeof(Member); -static constexpr size_t kSizeOfUncompressedMember = - sizeof(subtle::UncompressedMember); -#if defined(CPPGC_POINTER_COMPRESSION) -static constexpr size_t kSizeofCompressedMember = - sizeof(subtle::CompressedMember); -#endif // defined(CPPGC_POINTER_COMPRESSION) - -} // namespace internal - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_MEMBER_H_ diff --git a/NativeScript/napi/v8/include/cppgc/name-provider.h b/NativeScript/napi/v8/include/cppgc/name-provider.h deleted file mode 100644 index 216f6098..00000000 --- a/NativeScript/napi/v8/include/cppgc/name-provider.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_NAME_PROVIDER_H_ -#define INCLUDE_CPPGC_NAME_PROVIDER_H_ - -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -/** - * NameProvider allows for providing a human-readable name for garbage-collected - * objects. - * - * There's two cases of names to distinguish: - * a. Explicitly specified names via using NameProvider. Such names are always - * preserved in the system. - * b. Internal names that Oilpan infers from a C++ type on the class hierarchy - * of the object. This is not necessarily the type of the actually - * instantiated object. - * - * Depending on the build configuration, Oilpan may hide names, i.e., represent - * them with kHiddenName, of case b. to avoid exposing internal details. - */ -class V8_EXPORT NameProvider { - public: - /** - * Name that is used when hiding internals. - */ - static constexpr const char kHiddenName[] = "InternalNode"; - - /** - * Name that is used in case compiler support is missing for composing a name - * from C++ types. - */ - static constexpr const char kNoNameDeducible[] = ""; - - /** - * Indicating whether the build supports extracting C++ names as object names. - * - * @returns true if C++ names should be hidden and represented by kHiddenName. - */ - static constexpr bool SupportsCppClassNamesAsObjectNames() { -#if CPPGC_SUPPORTS_OBJECT_NAMES - return true; -#else // !CPPGC_SUPPORTS_OBJECT_NAMES - return false; -#endif // !CPPGC_SUPPORTS_OBJECT_NAMES - } - - virtual ~NameProvider() = default; - - /** - * Specifies a name for the garbage-collected object. Such names will never - * be hidden, as they are explicitly specified by the user of this API. - * - * @returns a human readable name for the object. - */ - virtual const char* GetHumanReadableName() const = 0; -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_NAME_PROVIDER_H_ diff --git a/NativeScript/napi/v8/include/cppgc/object-size-trait.h b/NativeScript/napi/v8/include/cppgc/object-size-trait.h deleted file mode 100644 index 35795596..00000000 --- a/NativeScript/napi/v8/include/cppgc/object-size-trait.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_ -#define INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_ - -#include - -#include "cppgc/type-traits.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -namespace internal { - -struct V8_EXPORT BaseObjectSizeTrait { - protected: - static size_t GetObjectSizeForGarbageCollected(const void*); - static size_t GetObjectSizeForGarbageCollectedMixin(const void*); -}; - -} // namespace internal - -namespace subtle { - -/** - * Trait specifying how to get the size of an object that was allocated using - * `MakeGarbageCollected()`. Also supports querying the size with an inner - * pointer to a mixin. - */ -template > -struct ObjectSizeTrait; - -template -struct ObjectSizeTrait : cppgc::internal::BaseObjectSizeTrait { - static_assert(sizeof(T), "T must be fully defined"); - static_assert(IsGarbageCollectedTypeV, - "T must be of type GarbageCollected or GarbageCollectedMixin"); - - static size_t GetSize(const T& object) { - return GetObjectSizeForGarbageCollected(&object); - } -}; - -template -struct ObjectSizeTrait : cppgc::internal::BaseObjectSizeTrait { - static_assert(sizeof(T), "T must be fully defined"); - - static size_t GetSize(const T& object) { - return GetObjectSizeForGarbageCollectedMixin(&object); - } -}; - -} // namespace subtle -} // namespace cppgc - -#endif // INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_ diff --git a/NativeScript/napi/v8/include/cppgc/persistent.h b/NativeScript/napi/v8/include/cppgc/persistent.h deleted file mode 100644 index 6eb1c659..00000000 --- a/NativeScript/napi/v8/include/cppgc/persistent.h +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_PERSISTENT_H_ -#define INCLUDE_CPPGC_PERSISTENT_H_ - -#include - -#include "cppgc/internal/persistent-node.h" -#include "cppgc/internal/pointer-policies.h" -#include "cppgc/sentinel-pointer.h" -#include "cppgc/source-location.h" -#include "cppgc/type-traits.h" -#include "cppgc/visitor.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { - -// PersistentBase always refers to the object as const object and defers to -// BasicPersistent on casting to the right type as needed. -class PersistentBase { - protected: - PersistentBase() = default; - explicit PersistentBase(const void* raw) : raw_(raw) {} - - const void* GetValue() const { return raw_; } - void SetValue(const void* value) { raw_ = value; } - - PersistentNode* GetNode() const { return node_; } - void SetNode(PersistentNode* node) { node_ = node; } - - // Performs a shallow clear which assumes that internal persistent nodes are - // destroyed elsewhere. - void ClearFromGC() const { - raw_ = nullptr; - node_ = nullptr; - } - - protected: - mutable const void* raw_ = nullptr; - mutable PersistentNode* node_ = nullptr; - - friend class PersistentRegionBase; -}; - -// The basic class from which all Persistent classes are generated. -template -class BasicPersistent final : public PersistentBase, - public LocationPolicy, - private WeaknessPolicy, - private CheckingPolicy { - public: - using typename WeaknessPolicy::IsStrongPersistent; - using PointeeType = T; - - // Null-state/sentinel constructors. - BasicPersistent( // NOLINT - const SourceLocation& loc = SourceLocation::Current()) - : LocationPolicy(loc) {} - - BasicPersistent(std::nullptr_t, // NOLINT - const SourceLocation& loc = SourceLocation::Current()) - : LocationPolicy(loc) {} - - BasicPersistent( // NOLINT - SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) - : PersistentBase(s), LocationPolicy(loc) {} - - // Raw value constructors. - BasicPersistent(T* raw, // NOLINT - const SourceLocation& loc = SourceLocation::Current()) - : PersistentBase(raw), LocationPolicy(loc) { - if (!IsValid()) return; - SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) - .AllocateNode(this, &TraceAsRoot)); - this->CheckPointer(Get()); - } - - BasicPersistent(T& raw, // NOLINT - const SourceLocation& loc = SourceLocation::Current()) - : BasicPersistent(&raw, loc) {} - - // Copy ctor. - BasicPersistent(const BasicPersistent& other, - const SourceLocation& loc = SourceLocation::Current()) - : BasicPersistent(other.Get(), loc) {} - - // Heterogeneous ctor. - template ::value>> - // NOLINTNEXTLINE - BasicPersistent( - const BasicPersistent& other, - const SourceLocation& loc = SourceLocation::Current()) - : BasicPersistent(other.Get(), loc) {} - - // Move ctor. The heterogeneous move ctor is not supported since e.g. - // persistent can't reuse persistent node from weak persistent. - BasicPersistent( - BasicPersistent&& other, - const SourceLocation& loc = SourceLocation::Current()) noexcept - : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) { - if (!IsValid()) return; - GetNode()->UpdateOwner(this); - other.SetValue(nullptr); - other.SetNode(nullptr); - this->CheckPointer(Get()); - } - - // Constructor from member. - template ::value>> - // NOLINTNEXTLINE - BasicPersistent(const internal::BasicMember< - U, MemberBarrierPolicy, MemberWeaknessTag, - MemberCheckingPolicy, MemberStorageType>& member, - const SourceLocation& loc = SourceLocation::Current()) - : BasicPersistent(member.Get(), loc) {} - - ~BasicPersistent() { Clear(); } - - // Copy assignment. - BasicPersistent& operator=(const BasicPersistent& other) { - return operator=(other.Get()); - } - - template ::value>> - BasicPersistent& operator=( - const BasicPersistent& other) { - return operator=(other.Get()); - } - - // Move assignment. - BasicPersistent& operator=(BasicPersistent&& other) noexcept { - if (this == &other) return *this; - Clear(); - PersistentBase::operator=(std::move(other)); - LocationPolicy::operator=(std::move(other)); - if (!IsValid()) return *this; - GetNode()->UpdateOwner(this); - other.SetValue(nullptr); - other.SetNode(nullptr); - this->CheckPointer(Get()); - return *this; - } - - // Assignment from member. - template ::value>> - BasicPersistent& operator=( - const internal::BasicMember& - member) { - return operator=(member.Get()); - } - - BasicPersistent& operator=(T* other) { - Assign(other); - return *this; - } - - BasicPersistent& operator=(std::nullptr_t) { - Clear(); - return *this; - } - - BasicPersistent& operator=(SentinelPointer s) { - Assign(s); - return *this; - } - - explicit operator bool() const { return Get(); } - // Historically we allow implicit conversions to T*. - // NOLINTNEXTLINE - operator T*() const { return Get(); } - T* operator->() const { return Get(); } - T& operator*() const { return *Get(); } - - // CFI cast exemption to allow passing SentinelPointer through T* and support - // heterogeneous assignments between different Member and Persistent handles - // based on their actual types. - V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { - // The const_cast below removes the constness from PersistentBase storage. - // The following static_cast re-adds any constness if specified through the - // user-visible template parameter T. - return static_cast(const_cast(GetValue())); - } - - void Clear() { - // Simplified version of `Assign()` to allow calling without a complete type - // `T`. - if (IsValid()) { - WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); - SetNode(nullptr); - } - SetValue(nullptr); - } - - T* Release() { - T* result = Get(); - Clear(); - return result; - } - - template - BasicPersistent - To() const { - return BasicPersistent(static_cast(Get())); - } - - private: - static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) { - root_visitor.Trace(*static_cast(ptr)); - } - - bool IsValid() const { - // Ideally, handling kSentinelPointer would be done by the embedder. On the - // other hand, having Persistent aware of it is beneficial since no node - // gets wasted. - return GetValue() != nullptr && GetValue() != kSentinelPointer; - } - - void Assign(T* ptr) { - if (IsValid()) { - if (ptr && ptr != kSentinelPointer) { - // Simply assign the pointer reusing the existing node. - SetValue(ptr); - this->CheckPointer(ptr); - return; - } - WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); - SetNode(nullptr); - } - SetValue(ptr); - if (!IsValid()) return; - SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) - .AllocateNode(this, &TraceAsRoot)); - this->CheckPointer(Get()); - } - - void ClearFromGC() const { - if (IsValid()) { - WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); - PersistentBase::ClearFromGC(); - } - } - - // Set Get() for details. - V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") - T* GetFromGC() const { - return static_cast(const_cast(GetValue())); - } - - friend class internal::RootVisitor; -}; - -template -bool operator==(const BasicPersistent& p1, - const BasicPersistent& p2) { - return p1.Get() == p2.Get(); -} - -template -bool operator!=(const BasicPersistent& p1, - const BasicPersistent& p2) { - return !(p1 == p2); -} - -template -bool operator==( - const BasicPersistent& - p, - const BasicMember& m) { - return p.Get() == m.Get(); -} - -template -bool operator!=( - const BasicPersistent& - p, - const BasicMember& m) { - return !(p == m); -} - -template -bool operator==( - const BasicMember& m, - const BasicPersistent& - p) { - return m.Get() == p.Get(); -} - -template -bool operator!=( - const BasicMember& m, - const BasicPersistent& - p) { - return !(m == p); -} - -template -struct IsWeak> : std::true_type {}; -} // namespace internal - -/** - * Persistent is a way to create a strong pointer from an off-heap object to - * another on-heap object. As long as the Persistent handle is alive the GC will - * keep the object pointed to alive. The Persistent handle is always a GC root - * from the point of view of the GC. Persistent must be constructed and - * destructed in the same thread. - */ -template -using Persistent = - internal::BasicPersistent; - -/** - * WeakPersistent is a way to create a weak pointer from an off-heap object to - * an on-heap object. The pointer is automatically cleared when the pointee gets - * collected. WeakPersistent must be constructed and destructed in the same - * thread. - */ -template -using WeakPersistent = - internal::BasicPersistent; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_PERSISTENT_H_ diff --git a/NativeScript/napi/v8/include/cppgc/platform.h b/NativeScript/napi/v8/include/cppgc/platform.h deleted file mode 100644 index ae96579d..00000000 --- a/NativeScript/napi/v8/include/cppgc/platform.h +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_PLATFORM_H_ -#define INCLUDE_CPPGC_PLATFORM_H_ - -#include - -#include "cppgc/source-location.h" -#include "v8-platform.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -// TODO(v8:10346): Create separate includes for concepts that are not -// V8-specific. -using IdleTask = v8::IdleTask; -using JobHandle = v8::JobHandle; -using JobDelegate = v8::JobDelegate; -using JobTask = v8::JobTask; -using PageAllocator = v8::PageAllocator; -using Task = v8::Task; -using TaskPriority = v8::TaskPriority; -using TaskRunner = v8::TaskRunner; -using TracingController = v8::TracingController; - -/** - * Platform interface used by Heap. Contains allocators and executors. - */ -class V8_EXPORT Platform { - public: - virtual ~Platform() = default; - - /** - * \returns the allocator used by cppgc to allocate its heap and various - * support structures. Returning nullptr results in using the `PageAllocator` - * provided by `cppgc::InitializeProcess()` instead. - */ - virtual PageAllocator* GetPageAllocator() = 0; - - /** - * Monotonically increasing time in seconds from an arbitrary fixed point in - * the past. This function is expected to return at least - * millisecond-precision values. For this reason, - * it is recommended that the fixed point be no further in the past than - * the epoch. - **/ - virtual double MonotonicallyIncreasingTime() = 0; - - /** - * Foreground task runner that should be used by a Heap. - */ - virtual std::shared_ptr GetForegroundTaskRunner() { - return nullptr; - } - - /** - * Posts `job_task` to run in parallel. Returns a `JobHandle` associated with - * the `Job`, which can be joined or canceled. - * This avoids degenerate cases: - * - Calling `CallOnWorkerThread()` for each work item, causing significant - * overhead. - * - Fixed number of `CallOnWorkerThread()` calls that split the work and - * might run for a long time. This is problematic when many components post - * "num cores" tasks and all expect to use all the cores. In these cases, - * the scheduler lacks context to be fair to multiple same-priority requests - * and/or ability to request lower priority work to yield when high priority - * work comes in. - * A canonical implementation of `job_task` looks like: - * \code - * class MyJobTask : public JobTask { - * public: - * MyJobTask(...) : worker_queue_(...) {} - * // JobTask implementation. - * void Run(JobDelegate* delegate) override { - * while (!delegate->ShouldYield()) { - * // Smallest unit of work. - * auto work_item = worker_queue_.TakeWorkItem(); // Thread safe. - * if (!work_item) return; - * ProcessWork(work_item); - * } - * } - * - * size_t GetMaxConcurrency() const override { - * return worker_queue_.GetSize(); // Thread safe. - * } - * }; - * - * // ... - * auto handle = PostJob(TaskPriority::kUserVisible, - * std::make_unique(...)); - * handle->Join(); - * \endcode - * - * `PostJob()` and methods of the returned JobHandle/JobDelegate, must never - * be called while holding a lock that could be acquired by `JobTask::Run()` - * or `JobTask::GetMaxConcurrency()` -- that could result in a deadlock. This - * is because (1) `JobTask::GetMaxConcurrency()` may be invoked while holding - * internal lock (A), hence `JobTask::GetMaxConcurrency()` can only use a lock - * (B) if that lock is *never* held while calling back into `JobHandle` from - * any thread (A=>B/B=>A deadlock) and (2) `JobTask::Run()` or - * `JobTask::GetMaxConcurrency()` may be invoked synchronously from - * `JobHandle` (B=>JobHandle::foo=>B deadlock). - * - * A sufficient `PostJob()` implementation that uses the default Job provided - * in libplatform looks like: - * \code - * std::unique_ptr PostJob( - * TaskPriority priority, std::unique_ptr job_task) override { - * return std::make_unique( - * std::make_shared( - * this, std::move(job_task), kNumThreads)); - * } - * \endcode - */ - virtual std::unique_ptr PostJob( - TaskPriority priority, std::unique_ptr job_task) { - return nullptr; - } - - /** - * Returns an instance of a `TracingController`. This must be non-nullptr. The - * default implementation returns an empty `TracingController` that consumes - * trace data without effect. - */ - virtual TracingController* GetTracingController(); -}; - -/** - * Process-global initialization of the garbage collector. Must be called before - * creating a Heap. - * - * Can be called multiple times when paired with `ShutdownProcess()`. - * - * \param page_allocator The allocator used for maintaining meta data. Must stay - * always alive and not change between multiple calls to InitializeProcess. If - * no allocator is provided, a default internal version will be used. - * \param desired_heap_size Desired amount of virtual address space to reserve - * for the heap, in bytes. Actual size will be clamped to minimum and maximum - * values based on compile-time settings and may be rounded up. If this - * parameter is zero, a default value will be used. - */ -V8_EXPORT void InitializeProcess(PageAllocator* page_allocator = nullptr, - size_t desired_heap_size = 0); - -/** - * Must be called after destroying the last used heap. Some process-global - * metadata may not be returned and reused upon a subsequent - * `InitializeProcess()` call. - */ -V8_EXPORT void ShutdownProcess(); - -namespace internal { - -V8_EXPORT void Fatal(const std::string& reason = std::string(), - const SourceLocation& = SourceLocation::Current()); - -} // namespace internal - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_PLATFORM_H_ diff --git a/NativeScript/napi/v8/include/cppgc/prefinalizer.h b/NativeScript/napi/v8/include/cppgc/prefinalizer.h deleted file mode 100644 index 51f2eac8..00000000 --- a/NativeScript/napi/v8/include/cppgc/prefinalizer.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_PREFINALIZER_H_ -#define INCLUDE_CPPGC_PREFINALIZER_H_ - -#include "cppgc/internal/compiler-specific.h" -#include "cppgc/liveness-broker.h" - -namespace cppgc { - -namespace internal { - -class V8_EXPORT PrefinalizerRegistration final { - public: - using Callback = bool (*)(const cppgc::LivenessBroker&, void*); - - PrefinalizerRegistration(void*, Callback); - - void* operator new(size_t, void* location) = delete; - void* operator new(size_t) = delete; -}; - -} // namespace internal - -/** - * Macro must be used in the private section of `Class` and registers a - * prefinalization callback `void Class::PreFinalizer()`. The callback is - * invoked on garbage collection after the collector has found an object to be - * dead. - * - * Callback properties: - * - The callback is invoked before a possible destructor for the corresponding - * object. - * - The callback may access the whole object graph, irrespective of whether - * objects are considered dead or alive. - * - The callback is invoked on the same thread as the object was created on. - * - * Example: - * \code - * class WithPrefinalizer : public GarbageCollected { - * CPPGC_USING_PRE_FINALIZER(WithPrefinalizer, Dispose); - * - * public: - * void Trace(Visitor*) const {} - * void Dispose() { prefinalizer_called = true; } - * ~WithPrefinalizer() { - * // prefinalizer_called == true - * } - * private: - * bool prefinalizer_called = false; - * }; - * \endcode - */ -#define CPPGC_USING_PRE_FINALIZER(Class, PreFinalizer) \ - public: \ - static bool InvokePreFinalizer(const cppgc::LivenessBroker& liveness_broker, \ - void* object) { \ - static_assert(cppgc::IsGarbageCollectedOrMixinTypeV, \ - "Only garbage collected objects can have prefinalizers"); \ - Class* self = static_cast(object); \ - if (liveness_broker.IsHeapObjectAlive(self)) return false; \ - self->PreFinalizer(); \ - return true; \ - } \ - \ - private: \ - CPPGC_NO_UNIQUE_ADDRESS cppgc::internal::PrefinalizerRegistration \ - prefinalizer_dummy_{this, Class::InvokePreFinalizer}; \ - static_assert(true, "Force semicolon.") - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_PREFINALIZER_H_ diff --git a/NativeScript/napi/v8/include/cppgc/process-heap-statistics.h b/NativeScript/napi/v8/include/cppgc/process-heap-statistics.h deleted file mode 100644 index 774cc92f..00000000 --- a/NativeScript/napi/v8/include/cppgc/process-heap-statistics.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_ -#define INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_ - -#include -#include - -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { -namespace internal { -class ProcessHeapStatisticsUpdater; -} // namespace internal - -class V8_EXPORT ProcessHeapStatistics final { - public: - static size_t TotalAllocatedObjectSize() { - return total_allocated_object_size_.load(std::memory_order_relaxed); - } - static size_t TotalAllocatedSpace() { - return total_allocated_space_.load(std::memory_order_relaxed); - } - - private: - static std::atomic_size_t total_allocated_space_; - static std::atomic_size_t total_allocated_object_size_; - - friend class internal::ProcessHeapStatisticsUpdater; -}; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_ diff --git a/NativeScript/napi/v8/include/cppgc/sentinel-pointer.h b/NativeScript/napi/v8/include/cppgc/sentinel-pointer.h deleted file mode 100644 index bee96c77..00000000 --- a/NativeScript/napi/v8/include/cppgc/sentinel-pointer.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_SENTINEL_POINTER_H_ -#define INCLUDE_CPPGC_SENTINEL_POINTER_H_ - -#include - -#include "cppgc/internal/api-constants.h" - -namespace cppgc { -namespace internal { - -// Special tag type used to denote some sentinel member. The semantics of the -// sentinel is defined by the embedder. -struct SentinelPointer { -#if defined(CPPGC_POINTER_COMPRESSION) - static constexpr intptr_t kSentinelValue = - 1 << api_constants::kPointerCompressionShift; -#else // !defined(CPPGC_POINTER_COMPRESSION) - static constexpr intptr_t kSentinelValue = 0b10; -#endif // !defined(CPPGC_POINTER_COMPRESSION) - template - operator T*() const { - return reinterpret_cast(kSentinelValue); - } - // Hidden friends. - friend bool operator==(SentinelPointer, SentinelPointer) { return true; } - friend bool operator!=(SentinelPointer, SentinelPointer) { return false; } -}; - -} // namespace internal - -constexpr internal::SentinelPointer kSentinelPointer; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_SENTINEL_POINTER_H_ diff --git a/NativeScript/napi/v8/include/cppgc/source-location.h b/NativeScript/napi/v8/include/cppgc/source-location.h deleted file mode 100644 index 0dc28aed..00000000 --- a/NativeScript/napi/v8/include/cppgc/source-location.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_SOURCE_LOCATION_H_ -#define INCLUDE_CPPGC_SOURCE_LOCATION_H_ - -#include "v8-source-location.h" - -namespace cppgc { - -using SourceLocation = v8::SourceLocation; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_SOURCE_LOCATION_H_ diff --git a/NativeScript/napi/v8/include/cppgc/testing.h b/NativeScript/napi/v8/include/cppgc/testing.h deleted file mode 100644 index bddd1fc1..00000000 --- a/NativeScript/napi/v8/include/cppgc/testing.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_TESTING_H_ -#define INCLUDE_CPPGC_TESTING_H_ - -#include "cppgc/common.h" -#include "cppgc/macros.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -class HeapHandle; - -/** - * Namespace contains testing helpers. - */ -namespace testing { - -/** - * Overrides the state of the stack with the provided value. Parameters passed - * to explicit garbage collection calls still take precedence. Must not be - * nested. - * - * This scope is useful to make the garbage collector consider the stack when - * tasks that invoke garbage collection (through the provided platform) contain - * interesting pointers on its stack. - */ -class V8_EXPORT V8_NODISCARD OverrideEmbedderStackStateScope final { - CPPGC_STACK_ALLOCATED(); - - public: - /** - * Constructs a scoped object that automatically enters and leaves the scope. - * - * \param heap_handle The corresponding heap. - */ - explicit OverrideEmbedderStackStateScope(HeapHandle& heap_handle, - EmbedderStackState state); - ~OverrideEmbedderStackStateScope(); - - OverrideEmbedderStackStateScope(const OverrideEmbedderStackStateScope&) = - delete; - OverrideEmbedderStackStateScope& operator=( - const OverrideEmbedderStackStateScope&) = delete; - - private: - HeapHandle& heap_handle_; -}; - -/** - * Testing interface for managed heaps that allows for controlling garbage - * collection timings. Embedders should use this class when testing the - * interaction of their code with incremental/concurrent garbage collection. - */ -class V8_EXPORT StandaloneTestingHeap final { - public: - explicit StandaloneTestingHeap(HeapHandle&); - - /** - * Start an incremental garbage collection. - */ - void StartGarbageCollection(); - - /** - * Perform an incremental step. This will also schedule concurrent steps if - * needed. - * - * \param stack_state The state of the stack during the step. - */ - bool PerformMarkingStep(EmbedderStackState stack_state); - - /** - * Finalize the current garbage collection cycle atomically. - * Assumes that garbage collection is in progress. - * - * \param stack_state The state of the stack for finalizing the garbage - * collection cycle. - */ - void FinalizeGarbageCollection(EmbedderStackState stack_state); - - /** - * Toggle main thread marking on/off. Allows to stress concurrent marking - * (e.g. to better detect data races). - * - * \param should_mark Denotes whether the main thread should contribute to - * marking. Defaults to true. - */ - void ToggleMainThreadMarking(bool should_mark); - - /** - * Force enable compaction for the next garbage collection cycle. - */ - void ForceCompactionForNextGarbageCollection(); - - private: - HeapHandle& heap_handle_; -}; - -V8_EXPORT bool IsHeapObjectOld(void*); - -} // namespace testing -} // namespace cppgc - -#endif // INCLUDE_CPPGC_TESTING_H_ diff --git a/NativeScript/napi/v8/include/cppgc/trace-trait.h b/NativeScript/napi/v8/include/cppgc/trace-trait.h deleted file mode 100644 index 5fc863d2..00000000 --- a/NativeScript/napi/v8/include/cppgc/trace-trait.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_TRACE_TRAIT_H_ -#define INCLUDE_CPPGC_TRACE_TRAIT_H_ - -#include - -#include "cppgc/type-traits.h" -#include "v8config.h" // NOLINT(build/include_directory) - -namespace cppgc { - -class Visitor; - -namespace internal { - -class RootVisitor; - -using TraceRootCallback = void (*)(RootVisitor&, const void* object); - -// Implementation of the default TraceTrait handling GarbageCollected and -// GarbageCollectedMixin. -template ::type>> -struct TraceTraitImpl; - -} // namespace internal - -/** - * Callback for invoking tracing on a given object. - * - * \param visitor The visitor to dispatch to. - * \param object The object to invoke tracing on. - */ -using TraceCallback = void (*)(Visitor* visitor, const void* object); - -/** - * Describes how to trace an object, i.e., how to visit all Oilpan-relevant - * fields of an object. - */ -struct TraceDescriptor { - /** - * Adjusted base pointer, i.e., the pointer to the class inheriting directly - * from GarbageCollected, of the object that is being traced. - */ - const void* base_object_payload; - /** - * Callback for tracing the object. - */ - TraceCallback callback; -}; - -/** - * Callback for getting a TraceDescriptor for a given address. - * - * \param address Possibly inner address of an object. - * \returns a TraceDescriptor for the provided address. - */ -using TraceDescriptorCallback = TraceDescriptor (*)(const void* address); - -namespace internal { - -struct V8_EXPORT TraceTraitFromInnerAddressImpl { - static TraceDescriptor GetTraceDescriptor(const void* address); -}; - -/** - * Trait specifying how the garbage collector processes an object of type T. - * - * Advanced users may override handling by creating a specialization for their - * type. - */ -template -struct TraceTraitBase { - static_assert(internal::IsTraceableV, "T must have a Trace() method"); - - /** - * Accessor for retrieving a TraceDescriptor to process an object of type T. - * - * \param self The object to be processed. - * \returns a TraceDescriptor to process the object. - */ - static TraceDescriptor GetTraceDescriptor(const void* self) { - return internal::TraceTraitImpl::GetTraceDescriptor( - static_cast(self)); - } - - /** - * Function invoking the tracing for an object of type T. - * - * \param visitor The visitor to dispatch to. - * \param self The object to invoke tracing on. - */ - static void Trace(Visitor* visitor, const void* self) { - static_cast(self)->Trace(visitor); - } -}; - -} // namespace internal - -template -struct TraceTrait : public internal::TraceTraitBase {}; - -namespace internal { - -template -struct TraceTraitImpl { - static_assert(IsGarbageCollectedTypeV, - "T must be of type GarbageCollected or GarbageCollectedMixin"); - static TraceDescriptor GetTraceDescriptor(const void* self) { - return {self, TraceTrait::Trace}; - } -}; - -template -struct TraceTraitImpl { - static TraceDescriptor GetTraceDescriptor(const void* self) { - return internal::TraceTraitFromInnerAddressImpl::GetTraceDescriptor(self); - } -}; - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_TRACE_TRAIT_H_ diff --git a/NativeScript/napi/v8/include/cppgc/type-traits.h b/NativeScript/napi/v8/include/cppgc/type-traits.h deleted file mode 100644 index 46514353..00000000 --- a/NativeScript/napi/v8/include/cppgc/type-traits.h +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_TYPE_TRAITS_H_ -#define INCLUDE_CPPGC_TYPE_TRAITS_H_ - -// This file should stay with minimal dependencies to allow embedder to check -// against Oilpan types without including any other parts. -#include -#include - -namespace cppgc { - -class Visitor; - -namespace internal { -template -class BasicMember; -struct DijkstraWriteBarrierPolicy; -struct NoWriteBarrierPolicy; -class StrongMemberTag; -class UntracedMemberTag; -class WeakMemberTag; - -// Not supposed to be specialized by the user. -template -struct IsWeak : std::false_type {}; - -// IsTraceMethodConst is used to verify that all Trace methods are marked as -// const. It is equivalent to IsTraceable but for a non-const object. -template -struct IsTraceMethodConst : std::false_type {}; - -template -struct IsTraceMethodConst().Trace( - std::declval()))>> : std::true_type { -}; - -template -struct IsTraceable : std::false_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct IsTraceable< - T, std::void_t().Trace(std::declval()))>> - : std::true_type { - // All Trace methods should be marked as const. If an object of type - // 'T' is traceable then any object of type 'const T' should also - // be traceable. - static_assert(IsTraceMethodConst(), - "Trace methods should be marked as const."); -}; - -template -constexpr bool IsTraceableV = IsTraceable::value; - -template -struct HasGarbageCollectedMixinTypeMarker : std::false_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct HasGarbageCollectedMixinTypeMarker< - T, std::void_t< - typename std::remove_const_t::IsGarbageCollectedMixinTypeMarker>> - : std::true_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct HasGarbageCollectedTypeMarker : std::false_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct HasGarbageCollectedTypeMarker< - T, - std::void_t::IsGarbageCollectedTypeMarker>> - : std::true_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template ::value, - bool = HasGarbageCollectedMixinTypeMarker::value> -struct IsGarbageCollectedMixinType : std::false_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct IsGarbageCollectedMixinType : std::true_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template ::value> -struct IsGarbageCollectedType : std::false_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct IsGarbageCollectedType : std::true_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct IsGarbageCollectedOrMixinType - : std::integral_constant::value || - IsGarbageCollectedMixinType::value> { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template ::value && - HasGarbageCollectedMixinTypeMarker::value)> -struct IsGarbageCollectedWithMixinType : std::false_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct IsGarbageCollectedWithMixinType : std::true_type { - static_assert(sizeof(T), "T must be fully defined"); -}; - -template -struct IsSubclassOfBasicMemberTemplate { - private: - template - static std::true_type SubclassCheck( - BasicMember*); - static std::false_type SubclassCheck(...); - - public: - static constexpr bool value = - decltype(SubclassCheck(std::declval()))::value; -}; - -template ::value> -struct IsMemberType : std::false_type {}; - -template -struct IsMemberType : std::true_type {}; - -template ::value> -struct IsWeakMemberType : std::false_type {}; - -template -struct IsWeakMemberType : std::true_type {}; - -template ::value> -struct IsUntracedMemberType : std::false_type {}; - -template -struct IsUntracedMemberType : std::true_type {}; - -template -struct IsComplete { - private: - template - static std::true_type IsSizeOfKnown(U*); - static std::false_type IsSizeOfKnown(...); - - public: - static constexpr bool value = - decltype(IsSizeOfKnown(std::declval()))::value; -}; - -template -constexpr bool IsDecayedSameV = - std::is_same_v, std::decay_t>; - -template -constexpr bool IsStrictlyBaseOfV = - std::is_base_of_v, std::decay_t> && - !IsDecayedSameV; - -} // namespace internal - -/** - * Value is true for types that inherit from `GarbageCollectedMixin` but not - * `GarbageCollected` (i.e., they are free mixins), and false otherwise. - */ -template -constexpr bool IsGarbageCollectedMixinTypeV = - internal::IsGarbageCollectedMixinType::value; - -/** - * Value is true for types that inherit from `GarbageCollected`, and false - * otherwise. - */ -template -constexpr bool IsGarbageCollectedTypeV = - internal::IsGarbageCollectedType::value; - -/** - * Value is true for types that inherit from either `GarbageCollected` or - * `GarbageCollectedMixin`, and false otherwise. - */ -template -constexpr bool IsGarbageCollectedOrMixinTypeV = - internal::IsGarbageCollectedOrMixinType::value; - -/** - * Value is true for types that inherit from `GarbageCollected` and - * `GarbageCollectedMixin`, and false otherwise. - */ -template -constexpr bool IsGarbageCollectedWithMixinTypeV = - internal::IsGarbageCollectedWithMixinType::value; - -/** - * Value is true for types of type `Member`, and false otherwise. - */ -template -constexpr bool IsMemberTypeV = internal::IsMemberType::value; - -/** - * Value is true for types of type `UntracedMember`, and false otherwise. - */ -template -constexpr bool IsUntracedMemberTypeV = internal::IsUntracedMemberType::value; - -/** - * Value is true for types of type `WeakMember`, and false otherwise. - */ -template -constexpr bool IsWeakMemberTypeV = internal::IsWeakMemberType::value; - -/** - * Value is true for types that are considered weak references, and false - * otherwise. - */ -template -constexpr bool IsWeakV = internal::IsWeak::value; - -/** - * Value is true for types that are complete, and false otherwise. - */ -template -constexpr bool IsCompleteV = internal::IsComplete::value; - -} // namespace cppgc - -#endif // INCLUDE_CPPGC_TYPE_TRAITS_H_ diff --git a/NativeScript/napi/v8/include/cppgc/visitor.h b/NativeScript/napi/v8/include/cppgc/visitor.h deleted file mode 100644 index 1d6b39a1..00000000 --- a/NativeScript/napi/v8/include/cppgc/visitor.h +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_CPPGC_VISITOR_H_ -#define INCLUDE_CPPGC_VISITOR_H_ - -#include - -#include "cppgc/custom-space.h" -#include "cppgc/ephemeron-pair.h" -#include "cppgc/garbage-collected.h" -#include "cppgc/internal/logging.h" -#include "cppgc/internal/member-storage.h" -#include "cppgc/internal/pointer-policies.h" -#include "cppgc/liveness-broker.h" -#include "cppgc/member.h" -#include "cppgc/sentinel-pointer.h" -#include "cppgc/source-location.h" -#include "cppgc/trace-trait.h" -#include "cppgc/type-traits.h" - -namespace cppgc { - -namespace internal { -template -class BasicCrossThreadPersistent; -template -class BasicPersistent; -class ConservativeTracingVisitor; -class VisitorBase; -class VisitorFactory; -} // namespace internal - -using WeakCallback = void (*)(const LivenessBroker&, const void*); - -/** - * Visitor passed to trace methods. All managed pointers must have called the - * Visitor's trace method on them. - * - * \code - * class Foo final : public GarbageCollected { - * public: - * void Trace(Visitor* visitor) const { - * visitor->Trace(foo_); - * visitor->Trace(weak_foo_); - * } - * private: - * Member foo_; - * WeakMember weak_foo_; - * }; - * \endcode - */ -class V8_EXPORT Visitor { - public: - class Key { - private: - Key() = default; - friend class internal::VisitorFactory; - }; - - explicit Visitor(Key) {} - - virtual ~Visitor() = default; - - /** - * Trace method for Member. - * - * \param member Member reference retaining an object. - */ - template - void Trace(const Member& member) { - const T* value = member.GetRawAtomic(); - CPPGC_DCHECK(value != kSentinelPointer); - TraceImpl(value); - } - - /** - * Trace method for WeakMember. - * - * \param weak_member WeakMember reference weakly retaining an object. - */ - template - void Trace(const WeakMember& weak_member) { - static_assert(sizeof(T), "Pointee type must be fully defined."); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "T must be GarbageCollected or GarbageCollectedMixin type"); - static_assert(!internal::IsAllocatedOnCompactableSpace::value, - "Weak references to compactable objects are not allowed"); - - const T* value = weak_member.GetRawAtomic(); - - // Bailout assumes that WeakMember emits write barrier. - if (!value) { - return; - } - - CPPGC_DCHECK(value != kSentinelPointer); - VisitWeak(value, TraceTrait::GetTraceDescriptor(value), - &HandleWeak>, &weak_member); - } - -#if defined(CPPGC_POINTER_COMPRESSION) - /** - * Trace method for UncompressedMember. - * - * \param member UncompressedMember reference retaining an object. - */ - template - void Trace(const subtle::UncompressedMember& member) { - const T* value = member.GetRawAtomic(); - CPPGC_DCHECK(value != kSentinelPointer); - TraceImpl(value); - } -#endif // defined(CPPGC_POINTER_COMPRESSION) - - template - void TraceMultiple(const subtle::UncompressedMember* start, size_t len) { - static_assert(sizeof(T), "Pointee type must be fully defined."); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "T must be GarbageCollected or GarbageCollectedMixin type"); - VisitMultipleUncompressedMember(start, len, - &TraceTrait::GetTraceDescriptor); - } - - template , subtle::UncompressedMember>>* = nullptr> - void TraceMultiple(const Member* start, size_t len) { - static_assert(sizeof(T), "Pointee type must be fully defined."); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "T must be GarbageCollected or GarbageCollectedMixin type"); -#if defined(CPPGC_POINTER_COMPRESSION) - static_assert(std::is_same_v, subtle::CompressedMember>, - "Member and CompressedMember must be the same."); - VisitMultipleCompressedMember(start, len, - &TraceTrait::GetTraceDescriptor); -#endif // defined(CPPGC_POINTER_COMPRESSION) - } - - /** - * Trace method for inlined objects that are not allocated themselves but - * otherwise follow managed heap layout and have a Trace() method. - * - * \param object reference of the inlined object. - */ - template - void Trace(const T& object) { -#if V8_ENABLE_CHECKS - // This object is embedded in potentially multiple nested objects. The - // outermost object must not be in construction as such objects are (a) not - // processed immediately, and (b) only processed conservatively if not - // otherwise possible. - CheckObjectNotInConstruction(&object); -#endif // V8_ENABLE_CHECKS - TraceTrait::Trace(this, &object); - } - - template - void TraceMultiple(const T* start, size_t len) { -#if V8_ENABLE_CHECKS - // This object is embedded in potentially multiple nested objects. The - // outermost object must not be in construction as such objects are (a) not - // processed immediately, and (b) only processed conservatively if not - // otherwise possible. - CheckObjectNotInConstruction(start); -#endif // V8_ENABLE_CHECKS - for (size_t i = 0; i < len; ++i) { - const T* object = &start[i]; - if constexpr (std::is_polymorphic_v) { - // The object's vtable may be uninitialized in which case the object is - // not traced. - if (*reinterpret_cast(object) == 0) continue; - } - TraceTrait::Trace(this, object); - } - } - - /** - * Registers a weak callback method on the object of type T. See - * LivenessBroker for an usage example. - * - * \param object of type T specifying a weak callback method. - */ - template - void RegisterWeakCallbackMethod(const T* object) { - RegisterWeakCallback(&WeakCallbackMethodDelegate, object); - } - - /** - * Trace method for EphemeronPair. - * - * \param ephemeron_pair EphemeronPair reference weakly retaining a key object - * and strongly retaining a value object in case the key object is alive. - */ - template - void Trace(const EphemeronPair& ephemeron_pair) { - TraceEphemeron(ephemeron_pair.key, &ephemeron_pair.value); - RegisterWeakCallbackMethod, - &EphemeronPair::ClearValueIfKeyIsDead>( - &ephemeron_pair); - } - - /** - * Trace method for a single ephemeron. Used for tracing a raw ephemeron in - * which the `key` and `value` are kept separately. - * - * \param weak_member_key WeakMember reference weakly retaining a key object. - * \param member_value Member reference with ephemeron semantics. - */ - template - void TraceEphemeron(const WeakMember& weak_member_key, - const Member* member_value) { - const KeyType* key = weak_member_key.GetRawAtomic(); - if (!key) return; - - // `value` must always be non-null. - CPPGC_DCHECK(member_value); - const ValueType* value = member_value->GetRawAtomic(); - if (!value) return; - - // KeyType and ValueType may refer to GarbageCollectedMixin. - TraceDescriptor value_desc = - TraceTrait::GetTraceDescriptor(value); - CPPGC_DCHECK(value_desc.base_object_payload); - const void* key_base_object_payload = - TraceTrait::GetTraceDescriptor(key).base_object_payload; - CPPGC_DCHECK(key_base_object_payload); - - VisitEphemeron(key_base_object_payload, value, value_desc); - } - - /** - * Trace method for a single ephemeron. Used for tracing a raw ephemeron in - * which the `key` and `value` are kept separately. Note that this overload - * is for non-GarbageCollected `value`s that can be traced though. - * - * \param key `WeakMember` reference weakly retaining a key object. - * \param value Reference weakly retaining a value object. Note that - * `ValueType` here should not be `Member`. It is expected that - * `TraceTrait::GetTraceDescriptor(value)` returns a - * `TraceDescriptor` with a null base pointer but a valid trace method. - */ - template - void TraceEphemeron(const WeakMember& weak_member_key, - const ValueType* value) { - static_assert(!IsGarbageCollectedOrMixinTypeV, - "garbage-collected types must use WeakMember and Member"); - const KeyType* key = weak_member_key.GetRawAtomic(); - if (!key) return; - - // `value` must always be non-null. - CPPGC_DCHECK(value); - TraceDescriptor value_desc = - TraceTrait::GetTraceDescriptor(value); - // `value_desc.base_object_payload` must be null as this override is only - // taken for non-garbage-collected values. - CPPGC_DCHECK(!value_desc.base_object_payload); - - // KeyType might be a GarbageCollectedMixin. - const void* key_base_object_payload = - TraceTrait::GetTraceDescriptor(key).base_object_payload; - CPPGC_DCHECK(key_base_object_payload); - - VisitEphemeron(key_base_object_payload, value, value_desc); - } - - /** - * Trace method that strongifies a WeakMember. - * - * \param weak_member WeakMember reference retaining an object. - */ - template - void TraceStrongly(const WeakMember& weak_member) { - const T* value = weak_member.GetRawAtomic(); - CPPGC_DCHECK(value != kSentinelPointer); - TraceImpl(value); - } - - /** - * Trace method for retaining containers strongly. - * - * \param object reference to the container. - */ - template - void TraceStrongContainer(const T* object) { - TraceImpl(object); - } - - /** - * Trace method for retaining containers weakly. Note that weak containers - * should emit write barriers. - * - * \param object reference to the container. - * \param callback to be invoked. - * \param callback_data custom data that is passed to the callback. - */ - template - void TraceWeakContainer(const T* object, WeakCallback callback, - const void* callback_data) { - if (!object) return; - VisitWeakContainer(object, TraceTrait::GetTraceDescriptor(object), - TraceTrait::GetWeakTraceDescriptor(object), callback, - callback_data); - } - - /** - * Registers a slot containing a reference to an object allocated on a - * compactable space. Such references maybe be arbitrarily moved by the GC. - * - * \param slot location of reference to object that might be moved by the GC. - * The slot must contain an uncompressed pointer. - */ - template - void RegisterMovableReference(const T** slot) { - static_assert(internal::IsAllocatedOnCompactableSpace::value, - "Only references to objects allocated on compactable spaces " - "should be registered as movable slots."); - static_assert(!IsGarbageCollectedMixinTypeV, - "Mixin types do not support compaction."); - HandleMovableReference(reinterpret_cast(slot)); - } - - /** - * Registers a weak callback that is invoked during garbage collection. - * - * \param callback to be invoked. - * \param data custom data that is passed to the callback. - */ - virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {} - - /** - * Defers tracing an object from a concurrent thread to the mutator thread. - * Should be called by Trace methods of types that are not safe to trace - * concurrently. - * - * \param parameter tells the trace callback which object was deferred. - * \param callback to be invoked for tracing on the mutator thread. - * \param deferred_size size of deferred object. - * - * \returns false if the object does not need to be deferred (i.e. currently - * traced on the mutator thread) and true otherwise (i.e. currently traced on - * a concurrent thread). - */ - virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent( - const void* parameter, TraceCallback callback, size_t deferred_size) { - // By default tracing is not deferred. - return false; - } - - protected: - virtual void Visit(const void* self, TraceDescriptor) {} - virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback, - const void* weak_member) {} - virtual void VisitEphemeron(const void* key, const void* value, - TraceDescriptor value_desc) {} - virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, - TraceDescriptor weak_desc, - WeakCallback callback, const void* data) {} - virtual void HandleMovableReference(const void**) {} - - virtual void VisitMultipleUncompressedMember( - const void* start, size_t len, - TraceDescriptorCallback get_trace_descriptor) { - // Default implementation merely delegates to Visit(). - const char* it = static_cast(start); - const char* end = it + len * internal::kSizeOfUncompressedMember; - for (; it < end; it += internal::kSizeOfUncompressedMember) { - const auto* current = reinterpret_cast(it); - const void* object = current->LoadAtomic(); - if (!object) continue; - - Visit(object, get_trace_descriptor(object)); - } - } - -#if defined(CPPGC_POINTER_COMPRESSION) - virtual void VisitMultipleCompressedMember( - const void* start, size_t len, - TraceDescriptorCallback get_trace_descriptor) { - // Default implementation merely delegates to Visit(). - const char* it = static_cast(start); - const char* end = it + len * internal::kSizeofCompressedMember; - for (; it < end; it += internal::kSizeofCompressedMember) { - const auto* current = - reinterpret_cast(it); - const void* object = current->LoadAtomic(); - if (!object) continue; - - Visit(object, get_trace_descriptor(object)); - } - } -#endif // defined(CPPGC_POINTER_COMPRESSION) - - private: - template - static void WeakCallbackMethodDelegate(const LivenessBroker& info, - const void* self) { - // Callback is registered through a potential const Trace method but needs - // to be able to modify fields. See HandleWeak. - (const_cast(static_cast(self))->*method)(info); - } - - template - static void HandleWeak(const LivenessBroker& info, const void* object) { - const PointerType* weak = static_cast(object); - if (!info.IsHeapObjectAlive(weak->GetFromGC())) { - weak->ClearFromGC(); - } - } - - template - void TraceImpl(const T* t) { - static_assert(sizeof(T), "Pointee type must be fully defined."); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "T must be GarbageCollected or GarbageCollectedMixin type"); - if (!t) { - return; - } - Visit(t, TraceTrait::GetTraceDescriptor(t)); - } - -#if V8_ENABLE_CHECKS - void CheckObjectNotInConstruction(const void* address); -#endif // V8_ENABLE_CHECKS - - template - friend class internal::BasicCrossThreadPersistent; - template - friend class internal::BasicPersistent; - friend class internal::ConservativeTracingVisitor; - friend class internal::VisitorBase; -}; - -namespace internal { - -class V8_EXPORT RootVisitor { - public: - explicit RootVisitor(Visitor::Key) {} - - virtual ~RootVisitor() = default; - - template * = nullptr> - void Trace(const AnyStrongPersistentType& p) { - using PointeeType = typename AnyStrongPersistentType::PointeeType; - const void* object = Extract(p); - if (!object) { - return; - } - VisitRoot(object, TraceTrait::GetTraceDescriptor(object), - p.Location()); - } - - template * = nullptr> - void Trace(const AnyWeakPersistentType& p) { - using PointeeType = typename AnyWeakPersistentType::PointeeType; - static_assert(!internal::IsAllocatedOnCompactableSpace::value, - "Weak references to compactable objects are not allowed"); - const void* object = Extract(p); - if (!object) { - return; - } - VisitWeakRoot(object, TraceTrait::GetTraceDescriptor(object), - &HandleWeak, &p, p.Location()); - } - - protected: - virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {} - virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, - const void* weak_root, const SourceLocation&) {} - - private: - template - static const void* Extract(AnyPersistentType& p) { - using PointeeType = typename AnyPersistentType::PointeeType; - static_assert(sizeof(PointeeType), - "Persistent's pointee type must be fully defined"); - static_assert(internal::IsGarbageCollectedOrMixinType::value, - "Persistent's pointee type must be GarbageCollected or " - "GarbageCollectedMixin"); - return p.GetFromGC(); - } - - template - static void HandleWeak(const LivenessBroker& info, const void* object) { - const PointerType* weak = static_cast(object); - if (!info.IsHeapObjectAlive(weak->GetFromGC())) { - weak->ClearFromGC(); - } - } -}; - -} // namespace internal -} // namespace cppgc - -#endif // INCLUDE_CPPGC_VISITOR_H_ diff --git a/NativeScript/napi/v8/include/js_protocol-1.2.json b/NativeScript/napi/v8/include/js_protocol-1.2.json deleted file mode 100644 index aff68062..00000000 --- a/NativeScript/napi/v8/include/js_protocol-1.2.json +++ /dev/null @@ -1,997 +0,0 @@ -{ - "version": { "major": "1", "minor": "2" }, - "domains": [ - { - "domain": "Schema", - "description": "Provides information about the protocol schema.", - "types": [ - { - "id": "Domain", - "type": "object", - "description": "Description of the protocol domain.", - "exported": true, - "properties": [ - { "name": "name", "type": "string", "description": "Domain name." }, - { "name": "version", "type": "string", "description": "Domain version." } - ] - } - ], - "commands": [ - { - "name": "getDomains", - "description": "Returns supported domains.", - "handlers": ["browser", "renderer"], - "returns": [ - { "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." } - ] - } - ] - }, - { - "domain": "Runtime", - "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", - "types": [ - { - "id": "ScriptId", - "type": "string", - "description": "Unique script identifier." - }, - { - "id": "RemoteObjectId", - "type": "string", - "description": "Unique object identifier." - }, - { - "id": "UnserializableValue", - "type": "string", - "enum": ["Infinity", "NaN", "-Infinity", "-0"], - "description": "Primitive value which cannot be JSON-stringified." - }, - { - "id": "RemoteObject", - "type": "object", - "description": "Mirror object referencing original JavaScript object.", - "exported": true, - "properties": [ - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error", "proxy", "promise", "typedarray"], "description": "Object subtype hint. Specified for object type values only." }, - { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for object type values only." }, - { "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." }, - { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have value, but gets this property." }, - { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, - { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for object type values only.", "experimental": true }, - { "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true} - ] - }, - { - "id": "CustomPreview", - "type": "object", - "experimental": true, - "properties": [ - { "name": "header", "type": "string"}, - { "name": "hasBody", "type": "boolean"}, - { "name": "formatterObjectId", "$ref": "RemoteObjectId"}, - { "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, - { "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } - ] - }, - { - "id": "ObjectPreview", - "type": "object", - "experimental": true, - "description": "Object containing abbreviated remote object value.", - "properties": [ - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." }, - { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, - { "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, - { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, - { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for map and set subtype values only." } - ] - }, - { - "id": "PropertyPreview", - "type": "object", - "experimental": true, - "properties": [ - { "name": "name", "type": "string", "description": "Property name." }, - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, - { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, - { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." } - ] - }, - { - "id": "EntryPreview", - "type": "object", - "experimental": true, - "properties": [ - { "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, - { "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } - ] - }, - { - "id": "PropertyDescriptor", - "type": "object", - "description": "Object property descriptor.", - "properties": [ - { "name": "name", "type": "string", "description": "Property name or symbol description." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, - { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, - { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or undefined if there is no getter (accessor descriptors only)." }, - { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or undefined if there is no setter (accessor descriptors only)." }, - { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, - { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, - { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." }, - { "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the symbol type." } - ] - }, - { - "id": "InternalPropertyDescriptor", - "type": "object", - "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", - "properties": [ - { "name": "name", "type": "string", "description": "Conventional property name." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } - ] - }, - { - "id": "CallArgument", - "type": "object", - "description": "Represents function call argument. Either remote object id objectId, primitive value, unserializable primitive value or neither of (for undefined) them should be specified.", - "properties": [ - { "name": "value", "type": "any", "optional": true, "description": "Primitive value." }, - { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." } - ] - }, - { - "id": "ExecutionContextId", - "type": "integer", - "description": "Id of an execution context." - }, - { - "id": "ExecutionContextDescription", - "type": "object", - "description": "Description of an isolated world.", - "properties": [ - { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, - { "name": "origin", "type": "string", "description": "Execution context origin." }, - { "name": "name", "type": "string", "description": "Human readable name describing given context." }, - { "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." } - ] - }, - { - "id": "ExceptionDetails", - "type": "object", - "description": "Detailed information about exception (or error) that was thrown during script compilation or execution.", - "properties": [ - { "name": "exceptionId", "type": "integer", "description": "Exception id." }, - { "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." }, - { "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." }, - { "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." }, - { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." }, - { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." }, - { "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." } - ] - }, - { - "id": "Timestamp", - "type": "number", - "description": "Number of milliseconds since epoch." - }, - { - "id": "CallFrame", - "type": "object", - "description": "Stack entry for runtime errors and assertions.", - "properties": [ - { "name": "functionName", "type": "string", "description": "JavaScript function name." }, - { "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, - { "name": "url", "type": "string", "description": "JavaScript script name or url." }, - { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number (0-based)." }, - { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number (0-based)." } - ] - }, - { - "id": "StackTrace", - "type": "object", - "description": "Call frames for assertions or error messages.", - "exported": true, - "properties": [ - { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, - { "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } - ] - } - ], - "commands": [ - { - "name": "evaluate", - "async": true, - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, - { "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." }, - { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Evaluates expression on global object." - }, - { - "name": "awaitPromise", - "async": true, - "parameters": [ - { "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."} - ], - "description": "Add handler to promise with given promise object id." - }, - { - "name": "callFunctionOn", - "async": true, - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, - { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, - { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, - { "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." }, - { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." - }, - { - "name": "getProperties", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, - { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, - { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, - { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Returns properties of a given object. Object group of the result is inherited from the target object." - }, - { - "name": "releaseObject", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } - ], - "description": "Releases remote object with given id." - }, - { - "name": "releaseObjectGroup", - "parameters": [ - { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } - ], - "description": "Releases all remote objects that belong to a given group." - }, - { - "name": "runIfWaitingForDebugger", - "description": "Tells inspected instance to run if it was waiting for debugger to attach." - }, - { - "name": "enable", - "description": "Enables reporting of execution contexts creation by means of executionContextCreated event. When the reporting gets enabled the event will be sent immediately for each existing execution context." - }, - { - "name": "disable", - "description": "Disables reporting of execution contexts creation." - }, - { - "name": "discardConsoleEntries", - "description": "Discards collected exceptions and console API calls." - }, - { - "name": "setCustomObjectFormatterEnabled", - "parameters": [ - { - "name": "enabled", - "type": "boolean" - } - ], - "experimental": true - }, - { - "name": "compileScript", - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to compile." }, - { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, - { "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." } - ], - "returns": [ - { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Compiles expression." - }, - { - "name": "runScript", - "async": true, - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }, - { "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Runs script with given id in a given context." - } - ], - "events": [ - { - "name": "executionContextCreated", - "parameters": [ - { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } - ], - "description": "Issued when new execution context is created." - }, - { - "name": "executionContextDestroyed", - "parameters": [ - { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } - ], - "description": "Issued when execution context is destroyed." - }, - { - "name": "executionContextsCleared", - "description": "Issued when all executionContexts were cleared in browser" - }, - { - "name": "exceptionThrown", - "description": "Issued when exception was thrown and unhandled.", - "parameters": [ - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails" } - ] - }, - { - "name": "exceptionRevoked", - "description": "Issued when unhandled exception was revoked.", - "parameters": [ - { "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." }, - { "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in exceptionUnhandled." } - ] - }, - { - "name": "consoleAPICalled", - "description": "Issued when console API was called.", - "parameters": [ - { "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Type of the call." }, - { "name": "args", "type": "array", "items": { "$ref": "RemoteObject" }, "description": "Call arguments." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." }, - { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." } - ] - }, - { - "name": "inspectRequested", - "description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).", - "parameters": [ - { "name": "object", "$ref": "RemoteObject" }, - { "name": "hints", "type": "object" } - ] - } - ] - }, - { - "domain": "Debugger", - "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", - "dependencies": ["Runtime"], - "types": [ - { - "id": "BreakpointId", - "type": "string", - "description": "Breakpoint identifier." - }, - { - "id": "CallFrameId", - "type": "string", - "description": "Call frame identifier." - }, - { - "id": "Location", - "type": "object", - "properties": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the Debugger.scriptParsed." }, - { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } - ], - "description": "Location in the source code." - }, - { - "id": "ScriptPosition", - "experimental": true, - "type": "object", - "properties": [ - { "name": "lineNumber", "type": "integer" }, - { "name": "columnNumber", "type": "integer" } - ], - "description": "Location in the source code." - }, - { - "id": "CallFrame", - "type": "object", - "properties": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, - { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, - { "name": "functionLocation", "$ref": "Location", "optional": true, "experimental": true, "description": "Location in the source code." }, - { "name": "location", "$ref": "Location", "description": "Location in the source code." }, - { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, - { "name": "this", "$ref": "Runtime.RemoteObject", "description": "this object for this call frame." }, - { "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." } - ], - "description": "JavaScript call frame. Array of call frames form the call stack." - }, - { - "id": "Scope", - "type": "object", - "properties": [ - { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." }, - { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For global and with scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, - { "name": "name", "type": "string", "optional": true }, - { "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" }, - { "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" } - ], - "description": "Scope description." - }, - { - "id": "SearchMatch", - "type": "object", - "description": "Search match for resource.", - "exported": true, - "properties": [ - { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, - { "name": "lineContent", "type": "string", "description": "Line with match content." } - ], - "experimental": true - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." - }, - { - "name": "disable", - "description": "Disables debugger for given page." - }, - { - "name": "setBreakpointsActive", - "parameters": [ - { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } - ], - "description": "Activates / deactivates all breakpoints on the page." - }, - { - "name": "setSkipAllPauses", - "parameters": [ - { "name": "skip", "type": "boolean", "description": "New value for skip pauses state." } - ], - "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." - }, - { - "name": "setBreakpointByUrl", - "parameters": [ - { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, - { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either url or urlRegex must be specified." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } - ], - "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in locations property. Further matching script parsing will result in subsequent breakpointResolved events issued. This logical breakpoint will survive page reloads." - }, - { - "name": "setBreakpoint", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } - ], - "description": "Sets JavaScript breakpoint at a given location." - }, - { - "name": "removeBreakpoint", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId" } - ], - "description": "Removes JavaScript breakpoint." - }, - { - "name": "continueToLocation", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to continue to." } - ], - "description": "Continues execution until specific location is reached." - }, - { - "name": "stepOver", - "description": "Steps over the statement." - }, - { - "name": "stepInto", - "description": "Steps into the function call." - }, - { - "name": "stepOut", - "description": "Steps out of the function call." - }, - { - "name": "pause", - "description": "Stops on the next JavaScript statement." - }, - { - "name": "resume", - "description": "Resumes JavaScript execution." - }, - { - "name": "searchInContent", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, - { "name": "query", "type": "string", "description": "String to search for." }, - { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, - { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } - ], - "experimental": true, - "description": "Searches for given string in script content." - }, - { - "name": "setScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, - { "name": "scriptSource", "type": "string", "description": "New content of the script." }, - { "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." } - ], - "returns": [ - { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, - { "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, - { "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." } - ], - "description": "Edits JavaScript source live." - }, - { - "name": "restartFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } - ], - "returns": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } - ], - "description": "Restarts particular call frame from the beginning." - }, - { - "name": "getScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } - ], - "returns": [ - { "name": "scriptSource", "type": "string", "description": "Script source." } - ], - "description": "Returns source for the script with given id." - }, - { - "name": "setPauseOnExceptions", - "parameters": [ - { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } - ], - "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is none." - }, - { - "name": "evaluateOnCallFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using releaseObjectGroup)." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, - { "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Evaluates expression on a given call frame." - }, - { - "name": "setVariableValue", - "parameters": [ - { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, - { "name": "variableName", "type": "string", "description": "Variable name." }, - { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } - ], - "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." - }, - { - "name": "setAsyncCallStackDepth", - "parameters": [ - { "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to 0 will effectively disable collecting async call stacks (default)." } - ], - "description": "Enables or disables async call stacks tracking." - }, - { - "name": "setBlackboxPatterns", - "parameters": [ - { "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } - ], - "experimental": true, - "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." - }, - { - "name": "setBlackboxedRanges", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, - { "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } - ], - "experimental": true, - "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." - } - ], - "events": [ - { - "name": "scriptParsed", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, - { "name": "hash", "type": "string", "description": "Content hash of the script."}, - { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, - { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "experimental": true }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true } - ], - "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." - }, - { - "name": "scriptFailedToParse", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, - { "name": "hash", "type": "string", "description": "Content hash of the script."}, - { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true } - ], - "description": "Fired when virtual machine fails to parse the script." - }, - { - "name": "breakpointResolved", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, - { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } - ], - "description": "Fired when breakpoint is resolved to an actual script and location." - }, - { - "name": "paused", - "parameters": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, - { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason.", "exported": true }, - { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, - { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } - ], - "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." - }, - { - "name": "resumed", - "description": "Fired when the virtual machine resumed execution." - } - ] - }, - { - "domain": "Console", - "description": "This domain is deprecated - use Runtime or Log instead.", - "dependencies": ["Runtime"], - "deprecated": true, - "types": [ - { - "id": "ConsoleMessage", - "type": "object", - "description": "Console message.", - "properties": [ - { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation", "worker"], "description": "Message source." }, - { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." }, - { "name": "text", "type": "string", "description": "Message text." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, - { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message (1-based)." }, - { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message (1-based)." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables console domain, sends the messages collected so far to the client by means of the messageAdded notification." - }, - { - "name": "disable", - "description": "Disables console domain, prevents further console messages from being reported to the client." - }, - { - "name": "clearMessages", - "description": "Does nothing." - } - ], - "events": [ - { - "name": "messageAdded", - "parameters": [ - { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } - ], - "description": "Issued when new console message is added." - } - ] - }, - { - "domain": "Profiler", - "dependencies": ["Runtime", "Debugger"], - "types": [ - { - "id": "ProfileNode", - "type": "object", - "description": "Profile node. Holds callsite information, execution statistics and child nodes.", - "properties": [ - { "name": "id", "type": "integer", "description": "Unique id of the node." }, - { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, - { "name": "hitCount", "type": "integer", "optional": true, "experimental": true, "description": "Number of samples where this node was on top of the call stack." }, - { "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." }, - { "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, - { "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "experimental": true, "description": "An array of source position ticks." } - ] - }, - { - "id": "Profile", - "type": "object", - "description": "Profile.", - "properties": [ - { "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." }, - { "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." }, - { "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." }, - { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, - { "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." } - ] - }, - { - "id": "PositionTickInfo", - "type": "object", - "experimental": true, - "description": "Specifies a number of samples attributed to a certain source position.", - "properties": [ - { "name": "line", "type": "integer", "description": "Source line number (1-based)." }, - { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } - ] - } - ], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "setSamplingInterval", - "parameters": [ - { "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } - ], - "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." - }, - { - "name": "start" - }, - { - "name": "stop", - "returns": [ - { "name": "profile", "$ref": "Profile", "description": "Recorded profile." } - ] - } - ], - "events": [ - { - "name": "consoleProfileStarted", - "parameters": [ - { "name": "id", "type": "string" }, - { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, - { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } - ], - "description": "Sent when new profile recodring is started using console.profile() call." - }, - { - "name": "consoleProfileFinished", - "parameters": [ - { "name": "id", "type": "string" }, - { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, - { "name": "profile", "$ref": "Profile" }, - { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } - ] - } - ] - }, - { - "domain": "HeapProfiler", - "dependencies": ["Runtime"], - "experimental": true, - "types": [ - { - "id": "HeapSnapshotObjectId", - "type": "string", - "description": "Heap snapshot object id." - }, - { - "id": "SamplingHeapProfileNode", - "type": "object", - "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", - "properties": [ - { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, - { "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, - { "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } - ] - }, - { - "id": "SamplingHeapProfile", - "type": "object", - "description": "Profile.", - "properties": [ - { "name": "head", "$ref": "SamplingHeapProfileNode" } - ] - } - ], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "startTrackingHeapObjects", - "parameters": [ - { "name": "trackAllocations", "type": "boolean", "optional": true } - ] - }, - { - "name": "stopTrackingHeapObjects", - "parameters": [ - { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } - ] - }, - { - "name": "takeHeapSnapshot", - "parameters": [ - { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } - ] - }, - { - "name": "collectGarbage" - }, - { - "name": "getObjectByHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } - ] - }, - { - "name": "addInspectedHeapObject", - "parameters": [ - { "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } - ], - "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." - }, - { - "name": "getHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } - ], - "returns": [ - { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } - ] - }, - { - "name": "startSampling", - "parameters": [ - { "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } - ] - }, - { - "name": "stopSampling", - "returns": [ - { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } - ] - } - ], - "events": [ - { - "name": "addHeapSnapshotChunk", - "parameters": [ - { "name": "chunk", "type": "string" } - ] - }, - { - "name": "resetProfiles" - }, - { - "name": "reportHeapSnapshotProgress", - "parameters": [ - { "name": "done", "type": "integer" }, - { "name": "total", "type": "integer" }, - { "name": "finished", "type": "boolean", "optional": true } - ] - }, - { - "name": "lastSeenObjectId", - "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", - "parameters": [ - { "name": "lastSeenObjectId", "type": "integer" }, - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "heapStatsUpdate", - "description": "If heap objects tracking has been started then backend may send update for one or more fragments", - "parameters": [ - { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} - ] - } - ] - }] -} diff --git a/NativeScript/napi/v8/include/js_protocol-1.3.json b/NativeScript/napi/v8/include/js_protocol-1.3.json deleted file mode 100644 index a998d461..00000000 --- a/NativeScript/napi/v8/include/js_protocol-1.3.json +++ /dev/null @@ -1,1159 +0,0 @@ -{ - "version": { "major": "1", "minor": "3" }, - "domains": [ - { - "domain": "Schema", - "description": "This domain is deprecated.", - "deprecated": true, - "types": [ - { - "id": "Domain", - "type": "object", - "description": "Description of the protocol domain.", - "properties": [ - { "name": "name", "type": "string", "description": "Domain name." }, - { "name": "version", "type": "string", "description": "Domain version." } - ] - } - ], - "commands": [ - { - "name": "getDomains", - "description": "Returns supported domains.", - "handlers": ["browser", "renderer"], - "returns": [ - { "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." } - ] - } - ] - }, - { - "domain": "Runtime", - "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", - "types": [ - { - "id": "ScriptId", - "type": "string", - "description": "Unique script identifier." - }, - { - "id": "RemoteObjectId", - "type": "string", - "description": "Unique object identifier." - }, - { - "id": "UnserializableValue", - "type": "string", - "enum": ["Infinity", "NaN", "-Infinity", "-0"], - "description": "Primitive value which cannot be JSON-stringified." - }, - { - "id": "RemoteObject", - "type": "object", - "description": "Mirror object referencing original JavaScript object.", - "properties": [ - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "weakmap", "weakset", "iterator", "generator", "error", "proxy", "promise", "typedarray"], "description": "Object subtype hint. Specified for object type values only." }, - { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for object type values only." }, - { "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." }, - { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have value, but gets this property." }, - { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, - { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for object type values only.", "experimental": true }, - { "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true} - ] - }, - { - "id": "CustomPreview", - "type": "object", - "experimental": true, - "properties": [ - { "name": "header", "type": "string"}, - { "name": "hasBody", "type": "boolean"}, - { "name": "formatterObjectId", "$ref": "RemoteObjectId"}, - { "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, - { "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } - ] - }, - { - "id": "ObjectPreview", - "type": "object", - "experimental": true, - "description": "Object containing abbreviated remote object value.", - "properties": [ - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "weakmap", "weakset", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." }, - { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, - { "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, - { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, - { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for map and set subtype values only." } - ] - }, - { - "id": "PropertyPreview", - "type": "object", - "experimental": true, - "properties": [ - { "name": "name", "type": "string", "description": "Property name." }, - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, - { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, - { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "weakmap", "weakset", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." } - ] - }, - { - "id": "EntryPreview", - "type": "object", - "experimental": true, - "properties": [ - { "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, - { "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } - ] - }, - { - "id": "PropertyDescriptor", - "type": "object", - "description": "Object property descriptor.", - "properties": [ - { "name": "name", "type": "string", "description": "Property name or symbol description." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, - { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, - { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or undefined if there is no getter (accessor descriptors only)." }, - { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or undefined if there is no setter (accessor descriptors only)." }, - { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, - { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, - { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." }, - { "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the symbol type." } - ] - }, - { - "id": "InternalPropertyDescriptor", - "type": "object", - "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", - "properties": [ - { "name": "name", "type": "string", "description": "Conventional property name." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } - ] - }, - { - "id": "CallArgument", - "type": "object", - "description": "Represents function call argument. Either remote object id objectId, primitive value, unserializable primitive value or neither of (for undefined) them should be specified.", - "properties": [ - { "name": "value", "type": "any", "optional": true, "description": "Primitive value or serializable javascript object." }, - { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." } - ] - }, - { - "id": "ExecutionContextId", - "type": "integer", - "description": "Id of an execution context." - }, - { - "id": "ExecutionContextDescription", - "type": "object", - "description": "Description of an isolated world.", - "properties": [ - { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, - { "name": "origin", "type": "string", "description": "Execution context origin." }, - { "name": "name", "type": "string", "description": "Human readable name describing given context." }, - { "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." } - ] - }, - { - "id": "ExceptionDetails", - "type": "object", - "description": "Detailed information about exception (or error) that was thrown during script compilation or execution.", - "properties": [ - { "name": "exceptionId", "type": "integer", "description": "Exception id." }, - { "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." }, - { "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." }, - { "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." }, - { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." }, - { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." }, - { "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." } - ] - }, - { - "id": "Timestamp", - "type": "number", - "description": "Number of milliseconds since epoch." - }, - { - "id": "CallFrame", - "type": "object", - "description": "Stack entry for runtime errors and assertions.", - "properties": [ - { "name": "functionName", "type": "string", "description": "JavaScript function name." }, - { "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, - { "name": "url", "type": "string", "description": "JavaScript script name or url." }, - { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number (0-based)." }, - { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number (0-based)." } - ] - }, - { - "id": "StackTrace", - "type": "object", - "description": "Call frames for assertions or error messages.", - "properties": [ - { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, - { "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." }, - { "name": "parentId", "$ref": "StackTraceId", "optional": true, "experimental": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } - ] - }, - { - "id": "UniqueDebuggerId", - "type": "string", - "description": "Unique identifier of current debugger.", - "experimental": true - }, - { - "id": "StackTraceId", - "type": "object", - "description": "If debuggerId is set stack trace comes from another debugger and can be resolved there. This allows to track cross-debugger calls. See Runtime.StackTrace and Debugger.paused for usages.", - "properties": [ - { "name": "id", "type": "string" }, - { "name": "debuggerId", "$ref": "UniqueDebuggerId", "optional": true } - ], - "experimental": true - } - ], - "commands": [ - { - "name": "evaluate", - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, - { "name": "userGesture", "type": "boolean", "optional": true, "description": "Whether execution should be treated as initiated by user in the UI." }, - { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should await for resulting value and return once awaited promise is resolved." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Evaluates expression on global object." - }, - { - "name": "awaitPromise", - "parameters": [ - { "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."} - ], - "description": "Add handler to promise with given promise object id." - }, - { - "name": "callFunctionOn", - "parameters": [ - { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Identifier of the object to call function on. Either objectId or executionContextId should be specified." }, - { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, - { "name": "userGesture", "type": "boolean", "optional": true, "description": "Whether execution should be treated as initiated by user in the UI." }, - { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should await for resulting value and return once awaited promise is resolved." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies execution context which global object will be used to call function on. Either executionContextId or objectId should be specified." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects. If objectGroup is not specified and objectId is, objectGroup will be inherited from object." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." - }, - { - "name": "getProperties", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, - { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, - { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, - { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Returns properties of a given object. Object group of the result is inherited from the target object." - }, - { - "name": "releaseObject", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } - ], - "description": "Releases remote object with given id." - }, - { - "name": "releaseObjectGroup", - "parameters": [ - { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } - ], - "description": "Releases all remote objects that belong to a given group." - }, - { - "name": "runIfWaitingForDebugger", - "description": "Tells inspected instance to run if it was waiting for debugger to attach." - }, - { - "name": "enable", - "description": "Enables reporting of execution contexts creation by means of executionContextCreated event. When the reporting gets enabled the event will be sent immediately for each existing execution context." - }, - { - "name": "disable", - "description": "Disables reporting of execution contexts creation." - }, - { - "name": "discardConsoleEntries", - "description": "Discards collected exceptions and console API calls." - }, - { - "name": "setCustomObjectFormatterEnabled", - "parameters": [ - { - "name": "enabled", - "type": "boolean" - } - ], - "experimental": true - }, - { - "name": "compileScript", - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to compile." }, - { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, - { "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." } - ], - "returns": [ - { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Compiles expression." - }, - { - "name": "runScript", - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }, - { "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should await for resulting value and return once awaited promise is resolved." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Runs script with given id in a given context." - }, - { - "name": "queryObjects", - "parameters": [ - { "name": "prototypeObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the prototype to return objects for." } - ], - "returns": [ - { "name": "objects", "$ref": "RemoteObject", "description": "Array with objects." } - ] - }, - { - "name": "globalLexicalScopeNames", - "parameters": [ - { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to lookup global scope variables." } - ], - "returns": [ - { "name": "names", "type": "array", "items": { "type": "string" } } - ], - "description": "Returns all let, const and class variables from global scope." - } - ], - "events": [ - { - "name": "executionContextCreated", - "parameters": [ - { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution context." } - ], - "description": "Issued when new execution context is created." - }, - { - "name": "executionContextDestroyed", - "parameters": [ - { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } - ], - "description": "Issued when execution context is destroyed." - }, - { - "name": "executionContextsCleared", - "description": "Issued when all executionContexts were cleared in browser" - }, - { - "name": "exceptionThrown", - "description": "Issued when exception was thrown and unhandled.", - "parameters": [ - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails" } - ] - }, - { - "name": "exceptionRevoked", - "description": "Issued when unhandled exception was revoked.", - "parameters": [ - { "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." }, - { "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in exceptionThrown." } - ] - }, - { - "name": "consoleAPICalled", - "description": "Issued when console API was called.", - "parameters": [ - { "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd", "count", "timeEnd"], "description": "Type of the call." }, - { "name": "args", "type": "array", "items": { "$ref": "RemoteObject" }, "description": "Call arguments." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." }, - { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." }, - { "name": "context", "type": "string", "optional": true, "experimental": true, "description": "Console context descriptor for calls on non-default console context (not console.*): 'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call on named context." } - ] - }, - { - "name": "inspectRequested", - "description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).", - "parameters": [ - { "name": "object", "$ref": "RemoteObject" }, - { "name": "hints", "type": "object" } - ] - } - ] - }, - { - "domain": "Debugger", - "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", - "dependencies": ["Runtime"], - "types": [ - { - "id": "BreakpointId", - "type": "string", - "description": "Breakpoint identifier." - }, - { - "id": "CallFrameId", - "type": "string", - "description": "Call frame identifier." - }, - { - "id": "Location", - "type": "object", - "properties": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the Debugger.scriptParsed." }, - { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } - ], - "description": "Location in the source code." - }, - { - "id": "ScriptPosition", - "experimental": true, - "type": "object", - "properties": [ - { "name": "lineNumber", "type": "integer" }, - { "name": "columnNumber", "type": "integer" } - ], - "description": "Location in the source code." - }, - { - "id": "CallFrame", - "type": "object", - "properties": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, - { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, - { "name": "functionLocation", "$ref": "Location", "optional": true, "description": "Location in the source code." }, - { "name": "location", "$ref": "Location", "description": "Location in the source code." }, - { "name": "url", "type": "string", "description": "JavaScript script name or url." }, - { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, - { "name": "this", "$ref": "Runtime.RemoteObject", "description": "this object for this call frame." }, - { "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." } - ], - "description": "JavaScript call frame. Array of call frames form the call stack." - }, - { - "id": "Scope", - "type": "object", - "properties": [ - { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script", "eval", "module"], "description": "Scope type." }, - { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For global and with scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, - { "name": "name", "type": "string", "optional": true }, - { "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" }, - { "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" } - ], - "description": "Scope description." - }, - { - "id": "SearchMatch", - "type": "object", - "description": "Search match for resource.", - "properties": [ - { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, - { "name": "lineContent", "type": "string", "description": "Line with match content." } - ] - }, - { - "id": "BreakLocation", - "type": "object", - "properties": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the Debugger.scriptParsed." }, - { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." }, - { "name": "type", "type": "string", "enum": [ "debuggerStatement", "call", "return" ], "optional": true } - ] - } - ], - "commands": [ - { - "name": "enable", - "returns": [ - { "name": "debuggerId", "$ref": "Runtime.UniqueDebuggerId", "experimental": true, "description": "Unique identifier of the debugger." } - ], - "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." - }, - { - "name": "disable", - "description": "Disables debugger for given page." - }, - { - "name": "setBreakpointsActive", - "parameters": [ - { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } - ], - "description": "Activates / deactivates all breakpoints on the page." - }, - { - "name": "setSkipAllPauses", - "parameters": [ - { "name": "skip", "type": "boolean", "description": "New value for skip pauses state." } - ], - "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." - }, - { - "name": "setBreakpointByUrl", - "parameters": [ - { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, - { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either url or urlRegex must be specified." }, - { "name": "scriptHash", "type": "string", "optional": true, "description": "Script hash of the resources to set breakpoint on." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } - ], - "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in locations property. Further matching script parsing will result in subsequent breakpointResolved events issued. This logical breakpoint will survive page reloads." - }, - { - "name": "setBreakpoint", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } - ], - "description": "Sets JavaScript breakpoint at a given location." - }, - { - "name": "removeBreakpoint", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId" } - ], - "description": "Removes JavaScript breakpoint." - }, - { - "name": "getPossibleBreakpoints", - "parameters": [ - { "name": "start", "$ref": "Location", "description": "Start of range to search possible breakpoint locations in." }, - { "name": "end", "$ref": "Location", "optional": true, "description": "End of range to search possible breakpoint locations in (excluding). When not specified, end of scripts is used as end of range." }, - { "name": "restrictToFunction", "type": "boolean", "optional": true, "description": "Only consider locations which are in the same (non-nested) function as start." } - ], - "returns": [ - { "name": "locations", "type": "array", "items": { "$ref": "BreakLocation" }, "description": "List of the possible breakpoint locations." } - ], - "description": "Returns possible locations for breakpoint. scriptId in start and end range locations should be the same." - }, - { - "name": "continueToLocation", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to continue to." }, - { "name": "targetCallFrames", "type": "string", "enum": ["any", "current"], "optional": true } - ], - "description": "Continues execution until specific location is reached." - }, - { - "name": "pauseOnAsyncCall", - "parameters": [ - { "name": "parentStackTraceId", "$ref": "Runtime.StackTraceId", "description": "Debugger will pause when async call with given stack trace is started." } - ], - "experimental": true - }, - { - "name": "stepOver", - "description": "Steps over the statement." - }, - { - "name": "stepInto", - "parameters": [ - { "name": "breakOnAsyncCall", "type": "boolean", "optional": true, "experimental": true, "description": "Debugger will issue additional Debugger.paused notification if any async task is scheduled before next pause." } - ], - "description": "Steps into the function call." - }, - { - "name": "stepOut", - "description": "Steps out of the function call." - }, - { - "name": "pause", - "description": "Stops on the next JavaScript statement." - }, - { - "name": "scheduleStepIntoAsync", - "description": "This method is deprecated - use Debugger.stepInto with breakOnAsyncCall and Debugger.pauseOnAsyncTask instead. Steps into next scheduled async task if any is scheduled before next pause. Returns success when async task is actually scheduled, returns error if no task were scheduled or another scheduleStepIntoAsync was called.", - "experimental": true - }, - { - "name": "resume", - "description": "Resumes JavaScript execution." - }, - { - "name": "getStackTrace", - "parameters": [ - { "name": "stackTraceId", "$ref": "Runtime.StackTraceId" } - ], - "returns": [ - { "name": "stackTrace", "$ref": "Runtime.StackTrace" } - ], - "description": "Returns stack trace with given stackTraceId.", - "experimental": true - }, - { - "name": "searchInContent", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, - { "name": "query", "type": "string", "description": "String to search for." }, - { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, - { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } - ], - "description": "Searches for given string in script content." - }, - { - "name": "setScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, - { "name": "scriptSource", "type": "string", "description": "New content of the script." }, - { "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." } - ], - "returns": [ - { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, - { "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, - { "name": "asyncStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Async stack trace, if any." }, - { "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." } - ], - "description": "Edits JavaScript source live." - }, - { - "name": "restartFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } - ], - "returns": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, - { "name": "asyncStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Async stack trace, if any." } - ], - "description": "Restarts particular call frame from the beginning." - }, - { - "name": "getScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } - ], - "returns": [ - { "name": "scriptSource", "type": "string", "description": "Script source." } - ], - "description": "Returns source for the script with given id." - }, - { - "name": "setPauseOnExceptions", - "parameters": [ - { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } - ], - "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is none." - }, - { - "name": "evaluateOnCallFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using releaseObjectGroup)." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." }, - { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, - { "name": "throwOnSideEffect", "type": "boolean", "optional": true, "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, - { "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Evaluates expression on a given call frame." - }, - { - "name": "setVariableValue", - "parameters": [ - { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, - { "name": "variableName", "type": "string", "description": "Variable name." }, - { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } - ], - "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." - }, - { - "name": "setReturnValue", - "parameters": [ - { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New return value." } - ], - "experimental": true, - "description": "Changes return value in top frame. Available only at return break position." - }, - { - "name": "setAsyncCallStackDepth", - "parameters": [ - { "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to 0 will effectively disable collecting async call stacks (default)." } - ], - "description": "Enables or disables async call stacks tracking." - }, - { - "name": "setBlackboxPatterns", - "parameters": [ - { "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } - ], - "experimental": true, - "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." - }, - { - "name": "setBlackboxedRanges", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, - { "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } - ], - "experimental": true, - "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." - } - ], - "events": [ - { - "name": "scriptParsed", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, - { "name": "hash", "type": "string", "description": "Content hash of the script."}, - { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, - { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "experimental": true }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL." }, - { "name": "isModule", "type": "boolean", "optional": true, "description": "True, if this script is ES6 module." }, - { "name": "length", "type": "integer", "optional": true, "description": "This script length." }, - { "name": "stackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript top stack frame of where the script parsed event was triggered if available.", "experimental": true } - ], - "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." - }, - { - "name": "scriptFailedToParse", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, - { "name": "hash", "type": "string", "description": "Content hash of the script."}, - { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL." }, - { "name": "isModule", "type": "boolean", "optional": true, "description": "True, if this script is ES6 module." }, - { "name": "length", "type": "integer", "optional": true, "description": "This script length." }, - { "name": "stackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript top stack frame of where the script parsed event was triggered if available.", "experimental": true } - ], - "description": "Fired when virtual machine fails to parse the script." - }, - { - "name": "breakpointResolved", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, - { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } - ], - "description": "Fired when breakpoint is resolved to an actual script and location." - }, - { - "name": "paused", - "parameters": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, - { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "OOM", "other", "ambiguous" ], "description": "Pause reason." }, - { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, - { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, - { "name": "asyncStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Async stack trace, if any." }, - { "name": "asyncCallStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Just scheduled async call will have this stack trace as parent stack during async execution. This field is available only after Debugger.stepInto call with breakOnAsynCall flag." } - ], - "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." - }, - { - "name": "resumed", - "description": "Fired when the virtual machine resumed execution." - } - ] - }, - { - "domain": "Console", - "description": "This domain is deprecated - use Runtime or Log instead.", - "dependencies": ["Runtime"], - "deprecated": true, - "types": [ - { - "id": "ConsoleMessage", - "type": "object", - "description": "Console message.", - "properties": [ - { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation", "worker"], "description": "Message source." }, - { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." }, - { "name": "text", "type": "string", "description": "Message text." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, - { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message (1-based)." }, - { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message (1-based)." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables console domain, sends the messages collected so far to the client by means of the messageAdded notification." - }, - { - "name": "disable", - "description": "Disables console domain, prevents further console messages from being reported to the client." - }, - { - "name": "clearMessages", - "description": "Does nothing." - } - ], - "events": [ - { - "name": "messageAdded", - "parameters": [ - { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } - ], - "description": "Issued when new console message is added." - } - ] - }, - { - "domain": "Profiler", - "dependencies": ["Runtime", "Debugger"], - "types": [ - { - "id": "ProfileNode", - "type": "object", - "description": "Profile node. Holds callsite information, execution statistics and child nodes.", - "properties": [ - { "name": "id", "type": "integer", "description": "Unique id of the node." }, - { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, - { "name": "hitCount", "type": "integer", "optional": true, "description": "Number of samples where this node was on top of the call stack." }, - { "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." }, - { "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, - { "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "description": "An array of source position ticks." } - ] - }, - { - "id": "Profile", - "type": "object", - "description": "Profile.", - "properties": [ - { "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." }, - { "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." }, - { "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." }, - { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, - { "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." } - ] - }, - { - "id": "PositionTickInfo", - "type": "object", - "description": "Specifies a number of samples attributed to a certain source position.", - "properties": [ - { "name": "line", "type": "integer", "description": "Source line number (1-based)." }, - { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } - ] - }, - { "id": "CoverageRange", - "type": "object", - "description": "Coverage data for a source range.", - "properties": [ - { "name": "startOffset", "type": "integer", "description": "JavaScript script source offset for the range start." }, - { "name": "endOffset", "type": "integer", "description": "JavaScript script source offset for the range end." }, - { "name": "count", "type": "integer", "description": "Collected execution count of the source range." } - ] - }, - { "id": "FunctionCoverage", - "type": "object", - "description": "Coverage data for a JavaScript function.", - "properties": [ - { "name": "functionName", "type": "string", "description": "JavaScript function name." }, - { "name": "ranges", "type": "array", "items": { "$ref": "CoverageRange" }, "description": "Source ranges inside the function with coverage data." }, - { "name": "isBlockCoverage", "type": "boolean", "description": "Whether coverage data for this function has block granularity." } - ] - }, - { - "id": "ScriptCoverage", - "type": "object", - "description": "Coverage data for a JavaScript script.", - "properties": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "JavaScript script id." }, - { "name": "url", "type": "string", "description": "JavaScript script name or url." }, - { "name": "functions", "type": "array", "items": { "$ref": "FunctionCoverage" }, "description": "Functions contained in the script that has coverage data." } - ] - } - ], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "setSamplingInterval", - "parameters": [ - { "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } - ], - "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." - }, - { - "name": "start" - }, - { - "name": "stop", - "returns": [ - { "name": "profile", "$ref": "Profile", "description": "Recorded profile." } - ] - }, - { - "name": "startPreciseCoverage", - "parameters": [ - { "name": "callCount", "type": "boolean", "optional": true, "description": "Collect accurate call counts beyond simple 'covered' or 'not covered'." }, - { "name": "detailed", "type": "boolean", "optional": true, "description": "Collect block-based coverage." } - ], - "description": "Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code coverage may be incomplete. Enabling prevents running optimized code and resets execution counters." - }, - { - "name": "stopPreciseCoverage", - "description": "Disable precise code coverage. Disabling releases unnecessary execution count records and allows executing optimized code." - }, - { - "name": "takePreciseCoverage", - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "ScriptCoverage" }, "description": "Coverage data for the current isolate." } - ], - "description": "Collect coverage data for the current isolate, and resets execution counters. Precise code coverage needs to have started." - }, - { - "name": "getBestEffortCoverage", - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "ScriptCoverage" }, "description": "Coverage data for the current isolate." } - ], - "description": "Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection." - } - ], - "events": [ - { - "name": "consoleProfileStarted", - "parameters": [ - { "name": "id", "type": "string" }, - { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, - { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } - ], - "description": "Sent when new profile recording is started using console.profile() call." - }, - { - "name": "consoleProfileFinished", - "parameters": [ - { "name": "id", "type": "string" }, - { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, - { "name": "profile", "$ref": "Profile" }, - { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } - ] - } - ] - }, - { - "domain": "HeapProfiler", - "dependencies": ["Runtime"], - "experimental": true, - "types": [ - { - "id": "HeapSnapshotObjectId", - "type": "string", - "description": "Heap snapshot object id." - }, - { - "id": "SamplingHeapProfileNode", - "type": "object", - "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", - "properties": [ - { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, - { "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, - { "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } - ] - }, - { - "id": "SamplingHeapProfile", - "type": "object", - "description": "Profile.", - "properties": [ - { "name": "head", "$ref": "SamplingHeapProfileNode" } - ] - } - ], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "startTrackingHeapObjects", - "parameters": [ - { "name": "trackAllocations", "type": "boolean", "optional": true } - ] - }, - { - "name": "stopTrackingHeapObjects", - "parameters": [ - { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } - ] - }, - { - "name": "takeHeapSnapshot", - "parameters": [ - { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } - ] - }, - { - "name": "collectGarbage" - }, - { - "name": "getObjectByHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } - ] - }, - { - "name": "addInspectedHeapObject", - "parameters": [ - { "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } - ], - "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." - }, - { - "name": "getHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } - ], - "returns": [ - { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } - ] - }, - { - "name": "startSampling", - "parameters": [ - { "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } - ] - }, - { - "name": "stopSampling", - "returns": [ - { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } - ] - }, - { - "name": "getSamplingProfile", - "returns": [ - { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Return the sampling profile being collected." } - ] - } - ], - "events": [ - { - "name": "addHeapSnapshotChunk", - "parameters": [ - { "name": "chunk", "type": "string" } - ] - }, - { - "name": "resetProfiles" - }, - { - "name": "reportHeapSnapshotProgress", - "parameters": [ - { "name": "done", "type": "integer" }, - { "name": "total", "type": "integer" }, - { "name": "finished", "type": "boolean", "optional": true } - ] - }, - { - "name": "lastSeenObjectId", - "description": "If heap objects tracking has been started then backend regularly sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", - "parameters": [ - { "name": "lastSeenObjectId", "type": "integer" }, - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "heapStatsUpdate", - "description": "If heap objects tracking has been started then backend may send update for one or more fragments", - "parameters": [ - { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} - ] - } - ] - }] -} diff --git a/NativeScript/napi/v8/include/js_protocol.pdl b/NativeScript/napi/v8/include/js_protocol.pdl deleted file mode 100644 index 4754f17c..00000000 --- a/NativeScript/napi/v8/include/js_protocol.pdl +++ /dev/null @@ -1,1806 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -version - major 1 - minor 3 - -# This domain is deprecated - use Runtime or Log instead. -deprecated domain Console - depends on Runtime - - # Console message. - type ConsoleMessage extends object - properties - # Message source. - enum source - xml - javascript - network - console-api - storage - appcache - rendering - security - other - deprecation - worker - # Message severity. - enum level - log - warning - error - debug - info - # Message text. - string text - # URL of the message origin. - optional string url - # Line number in the resource that generated this message (1-based). - optional integer line - # Column number in the resource that generated this message (1-based). - optional integer column - - # Does nothing. - command clearMessages - - # Disables console domain, prevents further console messages from being reported to the client. - command disable - - # Enables console domain, sends the messages collected so far to the client by means of the - # `messageAdded` notification. - command enable - - # Issued when new console message is added. - event messageAdded - parameters - # Console message that has been added. - ConsoleMessage message - -# Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing -# breakpoints, stepping through execution, exploring stack traces, etc. -domain Debugger - depends on Runtime - - # Breakpoint identifier. - type BreakpointId extends string - - # Call frame identifier. - type CallFrameId extends string - - # Location in the source code. - type Location extends object - properties - # Script identifier as reported in the `Debugger.scriptParsed`. - Runtime.ScriptId scriptId - # Line number in the script (0-based). - integer lineNumber - # Column number in the script (0-based). - optional integer columnNumber - - # Location in the source code. - experimental type ScriptPosition extends object - properties - integer lineNumber - integer columnNumber - - # Location range within one script. - experimental type LocationRange extends object - properties - Runtime.ScriptId scriptId - ScriptPosition start - ScriptPosition end - - # JavaScript call frame. Array of call frames form the call stack. - type CallFrame extends object - properties - # Call frame identifier. This identifier is only valid while the virtual machine is paused. - CallFrameId callFrameId - # Name of the JavaScript function called on this call frame. - string functionName - # Location in the source code. - optional Location functionLocation - # Location in the source code. - Location location - # JavaScript script name or url. - # Deprecated in favor of using the `location.scriptId` to resolve the URL via a previously - # sent `Debugger.scriptParsed` event. - deprecated string url - # Scope chain for this call frame. - array of Scope scopeChain - # `this` object for this call frame. - Runtime.RemoteObject this - # The value being returned, if the function is at return point. - optional Runtime.RemoteObject returnValue - # Valid only while the VM is paused and indicates whether this frame - # can be restarted or not. Note that a `true` value here does not - # guarantee that Debugger#restartFrame with this CallFrameId will be - # successful, but it is very likely. - experimental optional boolean canBeRestarted - - # Scope description. - type Scope extends object - properties - # Scope type. - enum type - global - local - with - closure - catch - block - script - eval - module - wasm-expression-stack - # Object representing the scope. For `global` and `with` scopes it represents the actual - # object; for the rest of the scopes, it is artificial transient object enumerating scope - # variables as its properties. - Runtime.RemoteObject object - optional string name - # Location in the source code where scope starts - optional Location startLocation - # Location in the source code where scope ends - optional Location endLocation - - # Search match for resource. - type SearchMatch extends object - properties - # Line number in resource content. - number lineNumber - # Line with match content. - string lineContent - - type BreakLocation extends object - properties - # Script identifier as reported in the `Debugger.scriptParsed`. - Runtime.ScriptId scriptId - # Line number in the script (0-based). - integer lineNumber - # Column number in the script (0-based). - optional integer columnNumber - optional enum type - debuggerStatement - call - return - - # Continues execution until specific location is reached. - command continueToLocation - parameters - # Location to continue to. - Location location - optional enum targetCallFrames - any - current - - # Disables debugger for given page. - command disable - - # Enables debugger for the given page. Clients should not assume that the debugging has been - # enabled until the result for this command is received. - command enable - parameters - # The maximum size in bytes of collected scripts (not referenced by other heap objects) - # the debugger can hold. Puts no limit if parameter is omitted. - experimental optional number maxScriptsCacheSize - returns - # Unique identifier of the debugger. - experimental Runtime.UniqueDebuggerId debuggerId - - # Evaluates expression on a given call frame. - command evaluateOnCallFrame - parameters - # Call frame identifier to evaluate on. - CallFrameId callFrameId - # Expression to evaluate. - string expression - # String object group name to put result into (allows rapid releasing resulting object handles - # using `releaseObjectGroup`). - optional string objectGroup - # Specifies whether command line API should be available to the evaluated expression, defaults - # to false. - optional boolean includeCommandLineAPI - # In silent mode exceptions thrown during evaluation are not reported and do not pause - # execution. Overrides `setPauseOnException` state. - optional boolean silent - # Whether the result is expected to be a JSON object that should be sent by value. - optional boolean returnByValue - # Whether preview should be generated for the result. - experimental optional boolean generatePreview - # Whether to throw an exception if side effect cannot be ruled out during evaluation. - optional boolean throwOnSideEffect - # Terminate execution after timing out (number of milliseconds). - experimental optional Runtime.TimeDelta timeout - returns - # Object wrapper for the evaluation result. - Runtime.RemoteObject result - # Exception details. - optional Runtime.ExceptionDetails exceptionDetails - - # Returns possible locations for breakpoint. scriptId in start and end range locations should be - # the same. - command getPossibleBreakpoints - parameters - # Start of range to search possible breakpoint locations in. - Location start - # End of range to search possible breakpoint locations in (excluding). When not specified, end - # of scripts is used as end of range. - optional Location end - # Only consider locations which are in the same (non-nested) function as start. - optional boolean restrictToFunction - returns - # List of the possible breakpoint locations. - array of BreakLocation locations - - # Returns source for the script with given id. - command getScriptSource - parameters - # Id of the script to get source for. - Runtime.ScriptId scriptId - returns - # Script source (empty in case of Wasm bytecode). - string scriptSource - # Wasm bytecode. - optional binary bytecode - - experimental type WasmDisassemblyChunk extends object - properties - # The next chunk of disassembled lines. - array of string lines - # The bytecode offsets describing the start of each line. - array of integer bytecodeOffsets - - experimental command disassembleWasmModule - parameters - # Id of the script to disassemble - Runtime.ScriptId scriptId - returns - # For large modules, return a stream from which additional chunks of - # disassembly can be read successively. - optional string streamId - # The total number of lines in the disassembly text. - integer totalNumberOfLines - # The offsets of all function bodies, in the format [start1, end1, - # start2, end2, ...] where all ends are exclusive. - array of integer functionBodyOffsets - # The first chunk of disassembly. - WasmDisassemblyChunk chunk - - # Disassemble the next chunk of lines for the module corresponding to the - # stream. If disassembly is complete, this API will invalidate the streamId - # and return an empty chunk. Any subsequent calls for the now invalid stream - # will return errors. - experimental command nextWasmDisassemblyChunk - parameters - string streamId - returns - # The next chunk of disassembly. - WasmDisassemblyChunk chunk - - # This command is deprecated. Use getScriptSource instead. - deprecated command getWasmBytecode - parameters - # Id of the Wasm script to get source for. - Runtime.ScriptId scriptId - returns - # Script source. - binary bytecode - - # Returns stack trace with given `stackTraceId`. - experimental command getStackTrace - parameters - Runtime.StackTraceId stackTraceId - returns - Runtime.StackTrace stackTrace - - # Stops on the next JavaScript statement. - command pause - - experimental deprecated command pauseOnAsyncCall - parameters - # Debugger will pause when async call with given stack trace is started. - Runtime.StackTraceId parentStackTraceId - - # Removes JavaScript breakpoint. - command removeBreakpoint - parameters - BreakpointId breakpointId - - # Restarts particular call frame from the beginning. The old, deprecated - # behavior of `restartFrame` is to stay paused and allow further CDP commands - # after a restart was scheduled. This can cause problems with restarting, so - # we now continue execution immediatly after it has been scheduled until we - # reach the beginning of the restarted frame. - # - # To stay back-wards compatible, `restartFrame` now expects a `mode` - # parameter to be present. If the `mode` parameter is missing, `restartFrame` - # errors out. - # - # The various return values are deprecated and `callFrames` is always empty. - # Use the call frames from the `Debugger#paused` events instead, that fires - # once V8 pauses at the beginning of the restarted function. - command restartFrame - parameters - # Call frame identifier to evaluate on. - CallFrameId callFrameId - # The `mode` parameter must be present and set to 'StepInto', otherwise - # `restartFrame` will error out. - experimental optional enum mode - # Pause at the beginning of the restarted function - StepInto - returns - # New stack trace. - deprecated array of CallFrame callFrames - # Async stack trace, if any. - deprecated optional Runtime.StackTrace asyncStackTrace - # Async stack trace, if any. - deprecated optional Runtime.StackTraceId asyncStackTraceId - - # Resumes JavaScript execution. - command resume - parameters - # Set to true to terminate execution upon resuming execution. In contrast - # to Runtime.terminateExecution, this will allows to execute further - # JavaScript (i.e. via evaluation) until execution of the paused code - # is actually resumed, at which point termination is triggered. - # If execution is currently not paused, this parameter has no effect. - optional boolean terminateOnResume - - # Searches for given string in script content. - command searchInContent - parameters - # Id of the script to search in. - Runtime.ScriptId scriptId - # String to search for. - string query - # If true, search is case sensitive. - optional boolean caseSensitive - # If true, treats string parameter as regex. - optional boolean isRegex - returns - # List of search matches. - array of SearchMatch result - - # Enables or disables async call stacks tracking. - command setAsyncCallStackDepth - parameters - # Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async - # call stacks (default). - integer maxDepth - - # Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in - # scripts with url matching one of the patterns. VM will try to leave blackboxed script by - # performing 'step in' several times, finally resorting to 'step out' if unsuccessful. - experimental command setBlackboxPatterns - parameters - # Array of regexps that will be used to check script url for blackbox state. - array of string patterns - - # Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted - # scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. - # Positions array contains positions where blackbox state is changed. First interval isn't - # blackboxed. Array should be sorted. - experimental command setBlackboxedRanges - parameters - # Id of the script. - Runtime.ScriptId scriptId - array of ScriptPosition positions - - # Sets JavaScript breakpoint at a given location. - command setBreakpoint - parameters - # Location to set breakpoint in. - Location location - # Expression to use as a breakpoint condition. When specified, debugger will only stop on the - # breakpoint if this expression evaluates to true. - optional string condition - returns - # Id of the created breakpoint for further reference. - BreakpointId breakpointId - # Location this breakpoint resolved into. - Location actualLocation - - # Sets instrumentation breakpoint. - command setInstrumentationBreakpoint - parameters - # Instrumentation name. - enum instrumentation - beforeScriptExecution - beforeScriptWithSourceMapExecution - returns - # Id of the created breakpoint for further reference. - BreakpointId breakpointId - - # Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this - # command is issued, all existing parsed scripts will have breakpoints resolved and returned in - # `locations` property. Further matching script parsing will result in subsequent - # `breakpointResolved` events issued. This logical breakpoint will survive page reloads. - command setBreakpointByUrl - parameters - # Line number to set breakpoint at. - integer lineNumber - # URL of the resources to set breakpoint on. - optional string url - # Regex pattern for the URLs of the resources to set breakpoints on. Either `url` or - # `urlRegex` must be specified. - optional string urlRegex - # Script hash of the resources to set breakpoint on. - optional string scriptHash - # Offset in the line to set breakpoint at. - optional integer columnNumber - # Expression to use as a breakpoint condition. When specified, debugger will only stop on the - # breakpoint if this expression evaluates to true. - optional string condition - returns - # Id of the created breakpoint for further reference. - BreakpointId breakpointId - # List of the locations this breakpoint resolved into upon addition. - array of Location locations - - # Sets JavaScript breakpoint before each call to the given function. - # If another function was created from the same source as a given one, - # calling it will also trigger the breakpoint. - experimental command setBreakpointOnFunctionCall - parameters - # Function object id. - Runtime.RemoteObjectId objectId - # Expression to use as a breakpoint condition. When specified, debugger will - # stop on the breakpoint if this expression evaluates to true. - optional string condition - returns - # Id of the created breakpoint for further reference. - BreakpointId breakpointId - - # Activates / deactivates all breakpoints on the page. - command setBreakpointsActive - parameters - # New value for breakpoints active state. - boolean active - - # Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions, - # or caught exceptions, no exceptions. Initial pause on exceptions state is `none`. - command setPauseOnExceptions - parameters - # Pause on exceptions mode. - enum state - none - caught - uncaught - all - - # Changes return value in top frame. Available only at return break position. - experimental command setReturnValue - parameters - # New return value. - Runtime.CallArgument newValue - - # Edits JavaScript source live. - # - # In general, functions that are currently on the stack can not be edited with - # a single exception: If the edited function is the top-most stack frame and - # that is the only activation of that function on the stack. In this case - # the live edit will be successful and a `Debugger.restartFrame` for the - # top-most function is automatically triggered. - command setScriptSource - parameters - # Id of the script to edit. - Runtime.ScriptId scriptId - # New content of the script. - string scriptSource - # If true the change will not actually be applied. Dry run may be used to get result - # description without actually modifying the code. - optional boolean dryRun - # If true, then `scriptSource` is allowed to change the function on top of the stack - # as long as the top-most stack frame is the only activation of that function. - experimental optional boolean allowTopFrameEditing - returns - # New stack trace in case editing has happened while VM was stopped. - deprecated optional array of CallFrame callFrames - # Whether current call stack was modified after applying the changes. - deprecated optional boolean stackChanged - # Async stack trace, if any. - deprecated optional Runtime.StackTrace asyncStackTrace - # Async stack trace, if any. - deprecated optional Runtime.StackTraceId asyncStackTraceId - # Whether the operation was successful or not. Only `Ok` denotes a - # successful live edit while the other enum variants denote why - # the live edit failed. - experimental enum status - Ok - CompileError - BlockedByActiveGenerator - BlockedByActiveFunction - BlockedByTopLevelEsModuleChange - # Exception details if any. Only present when `status` is `CompileError`. - optional Runtime.ExceptionDetails exceptionDetails - - # Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc). - command setSkipAllPauses - parameters - # New value for skip pauses state. - boolean skip - - # Changes value of variable in a callframe. Object-based scopes are not supported and must be - # mutated manually. - command setVariableValue - parameters - # 0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' - # scope types are allowed. Other scopes could be manipulated manually. - integer scopeNumber - # Variable name. - string variableName - # New variable value. - Runtime.CallArgument newValue - # Id of callframe that holds variable. - CallFrameId callFrameId - - # Steps into the function call. - command stepInto - parameters - # Debugger will pause on the execution of the first async task which was scheduled - # before next pause. - experimental optional boolean breakOnAsyncCall - # The skipList specifies location ranges that should be skipped on step into. - experimental optional array of LocationRange skipList - - # Steps out of the function call. - command stepOut - - # Steps over the statement. - command stepOver - parameters - # The skipList specifies location ranges that should be skipped on step over. - experimental optional array of LocationRange skipList - - # Fired when breakpoint is resolved to an actual script and location. - event breakpointResolved - parameters - # Breakpoint unique identifier. - BreakpointId breakpointId - # Actual breakpoint location. - Location location - - # Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria. - event paused - parameters - # Call stack the virtual machine stopped on. - array of CallFrame callFrames - # Pause reason. - enum reason - ambiguous - assert - CSPViolation - debugCommand - DOM - EventListener - exception - instrumentation - OOM - other - promiseRejection - XHR - step - # Object containing break-specific auxiliary properties. - optional object data - # Hit breakpoints IDs - optional array of string hitBreakpoints - # Async stack trace, if any. - optional Runtime.StackTrace asyncStackTrace - # Async stack trace, if any. - experimental optional Runtime.StackTraceId asyncStackTraceId - # Never present, will be removed. - experimental deprecated optional Runtime.StackTraceId asyncCallStackTraceId - - # Fired when the virtual machine resumed execution. - event resumed - - # Enum of possible script languages. - type ScriptLanguage extends string - enum - JavaScript - WebAssembly - - # Debug symbols available for a wasm script. - type DebugSymbols extends object - properties - # Type of the debug symbols. - enum type - None - SourceMap - EmbeddedDWARF - ExternalDWARF - # URL of the external symbol source. - optional string externalURL - - # Fired when virtual machine fails to parse the script. - event scriptFailedToParse - parameters - # Identifier of the script parsed. - Runtime.ScriptId scriptId - # URL or name of the script parsed (if any). - string url - # Line offset of the script within the resource with given URL (for script tags). - integer startLine - # Column offset of the script within the resource with given URL. - integer startColumn - # Last line of the script. - integer endLine - # Length of the last line of the script. - integer endColumn - # Specifies script creation context. - Runtime.ExecutionContextId executionContextId - # Content hash of the script, SHA-256. - string hash - # Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string} - optional object executionContextAuxData - # URL of source map associated with script (if any). - optional string sourceMapURL - # True, if this script has sourceURL. - optional boolean hasSourceURL - # True, if this script is ES6 module. - optional boolean isModule - # This script length. - optional integer length - # JavaScript top stack frame of where the script parsed event was triggered if available. - experimental optional Runtime.StackTrace stackTrace - # If the scriptLanguage is WebAssembly, the code section offset in the module. - experimental optional integer codeOffset - # The language of the script. - experimental optional Debugger.ScriptLanguage scriptLanguage - # The name the embedder supplied for this script. - experimental optional string embedderName - - # Fired when virtual machine parses script. This event is also fired for all known and uncollected - # scripts upon enabling debugger. - event scriptParsed - parameters - # Identifier of the script parsed. - Runtime.ScriptId scriptId - # URL or name of the script parsed (if any). - string url - # Line offset of the script within the resource with given URL (for script tags). - integer startLine - # Column offset of the script within the resource with given URL. - integer startColumn - # Last line of the script. - integer endLine - # Length of the last line of the script. - integer endColumn - # Specifies script creation context. - Runtime.ExecutionContextId executionContextId - # Content hash of the script, SHA-256. - string hash - # Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string} - optional object executionContextAuxData - # True, if this script is generated as a result of the live edit operation. - experimental optional boolean isLiveEdit - # URL of source map associated with script (if any). - optional string sourceMapURL - # True, if this script has sourceURL. - optional boolean hasSourceURL - # True, if this script is ES6 module. - optional boolean isModule - # This script length. - optional integer length - # JavaScript top stack frame of where the script parsed event was triggered if available. - experimental optional Runtime.StackTrace stackTrace - # If the scriptLanguage is WebAssembly, the code section offset in the module. - experimental optional integer codeOffset - # The language of the script. - experimental optional Debugger.ScriptLanguage scriptLanguage - # If the scriptLanguage is WebASsembly, the source of debug symbols for the module. - experimental optional Debugger.DebugSymbols debugSymbols - # The name the embedder supplied for this script. - experimental optional string embedderName - -experimental domain HeapProfiler - depends on Runtime - - # Heap snapshot object id. - type HeapSnapshotObjectId extends string - - # Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes. - type SamplingHeapProfileNode extends object - properties - # Function location. - Runtime.CallFrame callFrame - # Allocations size in bytes for the node excluding children. - number selfSize - # Node id. Ids are unique across all profiles collected between startSampling and stopSampling. - integer id - # Child nodes. - array of SamplingHeapProfileNode children - - # A single sample from a sampling profile. - type SamplingHeapProfileSample extends object - properties - # Allocation size in bytes attributed to the sample. - number size - # Id of the corresponding profile tree node. - integer nodeId - # Time-ordered sample ordinal number. It is unique across all profiles retrieved - # between startSampling and stopSampling. - number ordinal - - # Sampling profile. - type SamplingHeapProfile extends object - properties - SamplingHeapProfileNode head - array of SamplingHeapProfileSample samples - - # Enables console to refer to the node with given id via $x (see Command Line API for more details - # $x functions). - command addInspectedHeapObject - parameters - # Heap snapshot object id to be accessible by means of $x command line API. - HeapSnapshotObjectId heapObjectId - - command collectGarbage - - command disable - - command enable - - command getHeapObjectId - parameters - # Identifier of the object to get heap object id for. - Runtime.RemoteObjectId objectId - returns - # Id of the heap snapshot object corresponding to the passed remote object id. - HeapSnapshotObjectId heapSnapshotObjectId - - command getObjectByHeapObjectId - parameters - HeapSnapshotObjectId objectId - # Symbolic group name that can be used to release multiple objects. - optional string objectGroup - returns - # Evaluation result. - Runtime.RemoteObject result - - command getSamplingProfile - returns - # Return the sampling profile being collected. - SamplingHeapProfile profile - - command startSampling - parameters - # Average sample interval in bytes. Poisson distribution is used for the intervals. The - # default value is 32768 bytes. - optional number samplingInterval - # By default, the sampling heap profiler reports only objects which are - # still alive when the profile is returned via getSamplingProfile or - # stopSampling, which is useful for determining what functions contribute - # the most to steady-state memory usage. This flag instructs the sampling - # heap profiler to also include information about objects discarded by - # major GC, which will show which functions cause large temporary memory - # usage or long GC pauses. - optional boolean includeObjectsCollectedByMajorGC - # By default, the sampling heap profiler reports only objects which are - # still alive when the profile is returned via getSamplingProfile or - # stopSampling, which is useful for determining what functions contribute - # the most to steady-state memory usage. This flag instructs the sampling - # heap profiler to also include information about objects discarded by - # minor GC, which is useful when tuning a latency-sensitive application - # for minimal GC activity. - optional boolean includeObjectsCollectedByMinorGC - - command startTrackingHeapObjects - parameters - optional boolean trackAllocations - - command stopSampling - returns - # Recorded sampling heap profile. - SamplingHeapProfile profile - - command stopTrackingHeapObjects - parameters - # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken - # when the tracking is stopped. - optional boolean reportProgress - # Deprecated in favor of `exposeInternals`. - deprecated optional boolean treatGlobalObjectsAsRoots - # If true, numerical values are included in the snapshot - optional boolean captureNumericValue - # If true, exposes internals of the snapshot. - experimental optional boolean exposeInternals - - command takeHeapSnapshot - parameters - # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken. - optional boolean reportProgress - # If true, a raw snapshot without artificial roots will be generated. - # Deprecated in favor of `exposeInternals`. - deprecated optional boolean treatGlobalObjectsAsRoots - # If true, numerical values are included in the snapshot - optional boolean captureNumericValue - # If true, exposes internals of the snapshot. - experimental optional boolean exposeInternals - - event addHeapSnapshotChunk - parameters - string chunk - - # If heap objects tracking has been started then backend may send update for one or more fragments - event heapStatsUpdate - parameters - # An array of triplets. Each triplet describes a fragment. The first integer is the fragment - # index, the second integer is a total count of objects for the fragment, the third integer is - # a total size of the objects for the fragment. - array of integer statsUpdate - - # If heap objects tracking has been started then backend regularly sends a current value for last - # seen object id and corresponding timestamp. If the were changes in the heap since last event - # then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event. - event lastSeenObjectId - parameters - integer lastSeenObjectId - number timestamp - - event reportHeapSnapshotProgress - parameters - integer done - integer total - optional boolean finished - - event resetProfiles - -domain Profiler - depends on Runtime - depends on Debugger - - # Profile node. Holds callsite information, execution statistics and child nodes. - type ProfileNode extends object - properties - # Unique id of the node. - integer id - # Function location. - Runtime.CallFrame callFrame - # Number of samples where this node was on top of the call stack. - optional integer hitCount - # Child node ids. - optional array of integer children - # The reason of being not optimized. The function may be deoptimized or marked as don't - # optimize. - optional string deoptReason - # An array of source position ticks. - optional array of PositionTickInfo positionTicks - - # Profile. - type Profile extends object - properties - # The list of profile nodes. First item is the root node. - array of ProfileNode nodes - # Profiling start timestamp in microseconds. - number startTime - # Profiling end timestamp in microseconds. - number endTime - # Ids of samples top nodes. - optional array of integer samples - # Time intervals between adjacent samples in microseconds. The first delta is relative to the - # profile startTime. - optional array of integer timeDeltas - - # Specifies a number of samples attributed to a certain source position. - type PositionTickInfo extends object - properties - # Source line number (1-based). - integer line - # Number of samples attributed to the source line. - integer ticks - - # Coverage data for a source range. - type CoverageRange extends object - properties - # JavaScript script source offset for the range start. - integer startOffset - # JavaScript script source offset for the range end. - integer endOffset - # Collected execution count of the source range. - integer count - - # Coverage data for a JavaScript function. - type FunctionCoverage extends object - properties - # JavaScript function name. - string functionName - # Source ranges inside the function with coverage data. - array of CoverageRange ranges - # Whether coverage data for this function has block granularity. - boolean isBlockCoverage - - # Coverage data for a JavaScript script. - type ScriptCoverage extends object - properties - # JavaScript script id. - Runtime.ScriptId scriptId - # JavaScript script name or url. - string url - # Functions contained in the script that has coverage data. - array of FunctionCoverage functions - - command disable - - command enable - - # Collect coverage data for the current isolate. The coverage data may be incomplete due to - # garbage collection. - command getBestEffortCoverage - returns - # Coverage data for the current isolate. - array of ScriptCoverage result - - # Changes CPU profiler sampling interval. Must be called before CPU profiles recording started. - command setSamplingInterval - parameters - # New sampling interval in microseconds. - integer interval - - command start - - # Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code - # coverage may be incomplete. Enabling prevents running optimized code and resets execution - # counters. - command startPreciseCoverage - parameters - # Collect accurate call counts beyond simple 'covered' or 'not covered'. - optional boolean callCount - # Collect block-based coverage. - optional boolean detailed - # Allow the backend to send updates on its own initiative - optional boolean allowTriggeredUpdates - returns - # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. - number timestamp - - command stop - returns - # Recorded profile. - Profile profile - - # Disable precise code coverage. Disabling releases unnecessary execution count records and allows - # executing optimized code. - command stopPreciseCoverage - - # Collect coverage data for the current isolate, and resets execution counters. Precise code - # coverage needs to have started. - command takePreciseCoverage - returns - # Coverage data for the current isolate. - array of ScriptCoverage result - # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. - number timestamp - - event consoleProfileFinished - parameters - string id - # Location of console.profileEnd(). - Debugger.Location location - Profile profile - # Profile title passed as an argument to console.profile(). - optional string title - - # Sent when new profile recording is started using console.profile() call. - event consoleProfileStarted - parameters - string id - # Location of console.profile(). - Debugger.Location location - # Profile title passed as an argument to console.profile(). - optional string title - - # Reports coverage delta since the last poll (either from an event like this, or from - # `takePreciseCoverage` for the current isolate. May only be sent if precise code - # coverage has been started. This event can be trigged by the embedder to, for example, - # trigger collection of coverage data immediately at a certain point in time. - experimental event preciseCoverageDeltaUpdate - parameters - # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. - number timestamp - # Identifier for distinguishing coverage events. - string occasion - # Coverage data for the current isolate. - array of ScriptCoverage result - -# Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. -# Evaluation results are returned as mirror object that expose object type, string representation -# and unique identifier that can be used for further object reference. Original objects are -# maintained in memory unless they are either explicitly released or are released along with the -# other objects in their object group. -domain Runtime - - # Unique script identifier. - type ScriptId extends string - - # Represents options for serialization. Overrides `generatePreview` and `returnByValue`. - type SerializationOptions extends object - properties - enum serialization - # Whether the result should be deep-serialized. The result is put into - # `deepSerializedValue` and `ObjectId` is provided. - deep - # Whether the result is expected to be a JSON object which should be sent by value. - # The result is put either into `value` or into `unserializableValue`. Synonym of - # `returnByValue: true`. Overrides `returnByValue`. - json - # Only remote object id is put in the result. Same bahaviour as if no - # `serializationOptions`, `generatePreview` nor `returnByValue` are provided. - idOnly - - # Deep serialization depth. Default is full depth. Respected only in `deep` serialization mode. - optional integer maxDepth - - # Embedder-specific parameters. For example if connected to V8 in Chrome these control DOM - # serialization via `maxNodeDepth: integer` and `includeShadowTree: "none" | "open" | "all"`. - # Values can be only of type string or integer. - optional object additionalParameters - - # Represents deep serialized value. - type DeepSerializedValue extends object - properties - enum type - undefined - null - string - number - boolean - bigint - regexp - date - symbol - array - object - function - map - set - weakmap - weakset - error - proxy - promise - typedarray - arraybuffer - node - window - generator - optional any value - optional string objectId - # Set if value reference met more then once during serialization. In such - # case, value is provided only to one of the serialized values. Unique - # per value in the scope of one CDP call. - optional integer weakLocalObjectReference - - # Unique object identifier. - type RemoteObjectId extends string - - # Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`, - # `-Infinity`, and bigint literals. - type UnserializableValue extends string - - # Mirror object referencing original JavaScript object. - type RemoteObject extends object - properties - # Object type. - enum type - object - function - undefined - string - number - boolean - symbol - bigint - # Object subtype hint. Specified for `object` type values only. - # NOTE: If you change anything here, make sure to also update - # `subtype` in `ObjectPreview` and `PropertyPreview` below. - optional enum subtype - array - null - node - regexp - date - map - set - weakmap - weakset - iterator - generator - error - proxy - promise - typedarray - arraybuffer - dataview - webassemblymemory - wasmvalue - # Object class (constructor) name. Specified for `object` type values only. - optional string className - # Remote object value in case of primitive values or JSON values (if it was requested). - optional any value - # Primitive value which can not be JSON-stringified does not have `value`, but gets this - # property. - optional UnserializableValue unserializableValue - # String representation of the object. - optional string description - # Deep serialized value. - experimental optional DeepSerializedValue deepSerializedValue - # Unique object identifier (for non-primitive values). - optional RemoteObjectId objectId - # Preview containing abbreviated property values. Specified for `object` type values only. - experimental optional ObjectPreview preview - experimental optional CustomPreview customPreview - - experimental type CustomPreview extends object - properties - # The JSON-stringified result of formatter.header(object, config) call. - # It contains json ML array that represents RemoteObject. - string header - # If formatter returns true as a result of formatter.hasBody call then bodyGetterId will - # contain RemoteObjectId for the function that returns result of formatter.body(object, config) call. - # The result value is json ML array. - optional RemoteObjectId bodyGetterId - - # Object containing abbreviated remote object value. - experimental type ObjectPreview extends object - properties - # Object type. - enum type - object - function - undefined - string - number - boolean - symbol - bigint - # Object subtype hint. Specified for `object` type values only. - optional enum subtype - array - null - node - regexp - date - map - set - weakmap - weakset - iterator - generator - error - proxy - promise - typedarray - arraybuffer - dataview - webassemblymemory - wasmvalue - # String representation of the object. - optional string description - # True iff some of the properties or entries of the original object did not fit. - boolean overflow - # List of the properties. - array of PropertyPreview properties - # List of the entries. Specified for `map` and `set` subtype values only. - optional array of EntryPreview entries - - experimental type PropertyPreview extends object - properties - # Property name. - string name - # Object type. Accessor means that the property itself is an accessor property. - enum type - object - function - undefined - string - number - boolean - symbol - accessor - bigint - # User-friendly property value string. - optional string value - # Nested value preview. - optional ObjectPreview valuePreview - # Object subtype hint. Specified for `object` type values only. - optional enum subtype - array - null - node - regexp - date - map - set - weakmap - weakset - iterator - generator - error - proxy - promise - typedarray - arraybuffer - dataview - webassemblymemory - wasmvalue - - experimental type EntryPreview extends object - properties - # Preview of the key. Specified for map-like collection entries. - optional ObjectPreview key - # Preview of the value. - ObjectPreview value - - # Object property descriptor. - type PropertyDescriptor extends object - properties - # Property name or symbol description. - string name - # The value associated with the property. - optional RemoteObject value - # True if the value associated with the property may be changed (data descriptors only). - optional boolean writable - # A function which serves as a getter for the property, or `undefined` if there is no getter - # (accessor descriptors only). - optional RemoteObject get - # A function which serves as a setter for the property, or `undefined` if there is no setter - # (accessor descriptors only). - optional RemoteObject set - # True if the type of this property descriptor may be changed and if the property may be - # deleted from the corresponding object. - boolean configurable - # True if this property shows up during enumeration of the properties on the corresponding - # object. - boolean enumerable - # True if the result was thrown during the evaluation. - optional boolean wasThrown - # True if the property is owned for the object. - optional boolean isOwn - # Property symbol object, if the property is of the `symbol` type. - optional RemoteObject symbol - - # Object internal property descriptor. This property isn't normally visible in JavaScript code. - type InternalPropertyDescriptor extends object - properties - # Conventional property name. - string name - # The value associated with the property. - optional RemoteObject value - - # Object private field descriptor. - experimental type PrivatePropertyDescriptor extends object - properties - # Private property name. - string name - # The value associated with the private property. - optional RemoteObject value - # A function which serves as a getter for the private property, - # or `undefined` if there is no getter (accessor descriptors only). - optional RemoteObject get - # A function which serves as a setter for the private property, - # or `undefined` if there is no setter (accessor descriptors only). - optional RemoteObject set - - # Represents function call argument. Either remote object id `objectId`, primitive `value`, - # unserializable primitive value or neither of (for undefined) them should be specified. - type CallArgument extends object - properties - # Primitive value or serializable javascript object. - optional any value - # Primitive value which can not be JSON-stringified. - optional UnserializableValue unserializableValue - # Remote object handle. - optional RemoteObjectId objectId - - # Id of an execution context. - type ExecutionContextId extends integer - - # Description of an isolated world. - type ExecutionContextDescription extends object - properties - # Unique id of the execution context. It can be used to specify in which execution context - # script evaluation should be performed. - ExecutionContextId id - # Execution context origin. - string origin - # Human readable name describing given context. - string name - # A system-unique execution context identifier. Unlike the id, this is unique across - # multiple processes, so can be reliably used to identify specific context while backend - # performs a cross-process navigation. - experimental string uniqueId - # Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string} - optional object auxData - - # Detailed information about exception (or error) that was thrown during script compilation or - # execution. - type ExceptionDetails extends object - properties - # Exception id. - integer exceptionId - # Exception text, which should be used together with exception object when available. - string text - # Line number of the exception location (0-based). - integer lineNumber - # Column number of the exception location (0-based). - integer columnNumber - # Script ID of the exception location. - optional ScriptId scriptId - # URL of the exception location, to be used when the script was not reported. - optional string url - # JavaScript stack trace if available. - optional StackTrace stackTrace - # Exception object if available. - optional RemoteObject exception - # Identifier of the context where exception happened. - optional ExecutionContextId executionContextId - # Dictionary with entries of meta data that the client associated - # with this exception, such as information about associated network - # requests, etc. - experimental optional object exceptionMetaData - - # Number of milliseconds since epoch. - type Timestamp extends number - - # Number of milliseconds. - type TimeDelta extends number - - # Stack entry for runtime errors and assertions. - type CallFrame extends object - properties - # JavaScript function name. - string functionName - # JavaScript script id. - ScriptId scriptId - # JavaScript script name or url. - string url - # JavaScript script line number (0-based). - integer lineNumber - # JavaScript script column number (0-based). - integer columnNumber - - # Call frames for assertions or error messages. - type StackTrace extends object - properties - # String label of this stack trace. For async traces this may be a name of the function that - # initiated the async call. - optional string description - # JavaScript function name. - array of CallFrame callFrames - # Asynchronous JavaScript stack trace that preceded this stack, if available. - optional StackTrace parent - # Asynchronous JavaScript stack trace that preceded this stack, if available. - experimental optional StackTraceId parentId - - # Unique identifier of current debugger. - experimental type UniqueDebuggerId extends string - - # If `debuggerId` is set stack trace comes from another debugger and can be resolved there. This - # allows to track cross-debugger calls. See `Runtime.StackTrace` and `Debugger.paused` for usages. - experimental type StackTraceId extends object - properties - string id - optional UniqueDebuggerId debuggerId - - # Add handler to promise with given promise object id. - command awaitPromise - parameters - # Identifier of the promise. - RemoteObjectId promiseObjectId - # Whether the result is expected to be a JSON object that should be sent by value. - optional boolean returnByValue - # Whether preview should be generated for the result. - optional boolean generatePreview - returns - # Promise result. Will contain rejected value if promise was rejected. - RemoteObject result - # Exception details if stack strace is available. - optional ExceptionDetails exceptionDetails - - # Calls function with given declaration on the given object. Object group of the result is - # inherited from the target object. - command callFunctionOn - parameters - # Declaration of the function to call. - string functionDeclaration - # Identifier of the object to call function on. Either objectId or executionContextId should - # be specified. - optional RemoteObjectId objectId - # Call arguments. All call arguments must belong to the same JavaScript world as the target - # object. - optional array of CallArgument arguments - # In silent mode exceptions thrown during evaluation are not reported and do not pause - # execution. Overrides `setPauseOnException` state. - optional boolean silent - # Whether the result is expected to be a JSON object which should be sent by value. - # Can be overriden by `serializationOptions`. - optional boolean returnByValue - # Whether preview should be generated for the result. - experimental optional boolean generatePreview - # Whether execution should be treated as initiated by user in the UI. - optional boolean userGesture - # Whether execution should `await` for resulting value and return once awaited promise is - # resolved. - optional boolean awaitPromise - # Specifies execution context which global object will be used to call function on. Either - # executionContextId or objectId should be specified. - optional ExecutionContextId executionContextId - # Symbolic group name that can be used to release multiple objects. If objectGroup is not - # specified and objectId is, objectGroup will be inherited from object. - optional string objectGroup - # Whether to throw an exception if side effect cannot be ruled out during evaluation. - experimental optional boolean throwOnSideEffect - # An alternative way to specify the execution context to call function on. - # Compared to contextId that may be reused across processes, this is guaranteed to be - # system-unique, so it can be used to prevent accidental function call - # in context different than intended (e.g. as a result of navigation across process - # boundaries). - # This is mutually exclusive with `executionContextId`. - experimental optional string uniqueContextId - # Specifies the result serialization. If provided, overrides - # `generatePreview` and `returnByValue`. - experimental optional SerializationOptions serializationOptions - - returns - # Call result. - RemoteObject result - # Exception details. - optional ExceptionDetails exceptionDetails - - # Compiles expression. - command compileScript - parameters - # Expression to compile. - string expression - # Source url to be set for the script. - string sourceURL - # Specifies whether the compiled script should be persisted. - boolean persistScript - # Specifies in which execution context to perform script run. If the parameter is omitted the - # evaluation will be performed in the context of the inspected page. - optional ExecutionContextId executionContextId - returns - # Id of the script. - optional ScriptId scriptId - # Exception details. - optional ExceptionDetails exceptionDetails - - # Disables reporting of execution contexts creation. - command disable - - # Discards collected exceptions and console API calls. - command discardConsoleEntries - - # Enables reporting of execution contexts creation by means of `executionContextCreated` event. - # When the reporting gets enabled the event will be sent immediately for each existing execution - # context. - command enable - - # Evaluates expression on global object. - command evaluate - parameters - # Expression to evaluate. - string expression - # Symbolic group name that can be used to release multiple objects. - optional string objectGroup - # Determines whether Command Line API should be available during the evaluation. - optional boolean includeCommandLineAPI - # In silent mode exceptions thrown during evaluation are not reported and do not pause - # execution. Overrides `setPauseOnException` state. - optional boolean silent - # Specifies in which execution context to perform evaluation. If the parameter is omitted the - # evaluation will be performed in the context of the inspected page. - # This is mutually exclusive with `uniqueContextId`, which offers an - # alternative way to identify the execution context that is more reliable - # in a multi-process environment. - optional ExecutionContextId contextId - # Whether the result is expected to be a JSON object that should be sent by value. - optional boolean returnByValue - # Whether preview should be generated for the result. - experimental optional boolean generatePreview - # Whether execution should be treated as initiated by user in the UI. - optional boolean userGesture - # Whether execution should `await` for resulting value and return once awaited promise is - # resolved. - optional boolean awaitPromise - # Whether to throw an exception if side effect cannot be ruled out during evaluation. - # This implies `disableBreaks` below. - experimental optional boolean throwOnSideEffect - # Terminate execution after timing out (number of milliseconds). - experimental optional TimeDelta timeout - # Disable breakpoints during execution. - experimental optional boolean disableBreaks - # Setting this flag to true enables `let` re-declaration and top-level `await`. - # Note that `let` variables can only be re-declared if they originate from - # `replMode` themselves. - experimental optional boolean replMode - # The Content Security Policy (CSP) for the target might block 'unsafe-eval' - # which includes eval(), Function(), setTimeout() and setInterval() - # when called with non-callable arguments. This flag bypasses CSP for this - # evaluation and allows unsafe-eval. Defaults to true. - experimental optional boolean allowUnsafeEvalBlockedByCSP - # An alternative way to specify the execution context to evaluate in. - # Compared to contextId that may be reused across processes, this is guaranteed to be - # system-unique, so it can be used to prevent accidental evaluation of the expression - # in context different than intended (e.g. as a result of navigation across process - # boundaries). - # This is mutually exclusive with `contextId`. - experimental optional string uniqueContextId - # Specifies the result serialization. If provided, overrides - # `generatePreview` and `returnByValue`. - experimental optional SerializationOptions serializationOptions - returns - # Evaluation result. - RemoteObject result - # Exception details. - optional ExceptionDetails exceptionDetails - - # Returns the isolate id. - experimental command getIsolateId - returns - # The isolate id. - string id - - # Returns the JavaScript heap usage. - # It is the total usage of the corresponding isolate not scoped to a particular Runtime. - experimental command getHeapUsage - returns - # Used heap size in bytes. - number usedSize - # Allocated heap size in bytes. - number totalSize - - # Returns properties of a given object. Object group of the result is inherited from the target - # object. - command getProperties - parameters - # Identifier of the object to return properties for. - RemoteObjectId objectId - # If true, returns properties belonging only to the element itself, not to its prototype - # chain. - optional boolean ownProperties - # If true, returns accessor properties (with getter/setter) only; internal properties are not - # returned either. - experimental optional boolean accessorPropertiesOnly - # Whether preview should be generated for the results. - experimental optional boolean generatePreview - # If true, returns non-indexed properties only. - experimental optional boolean nonIndexedPropertiesOnly - returns - # Object properties. - array of PropertyDescriptor result - # Internal object properties (only of the element itself). - optional array of InternalPropertyDescriptor internalProperties - # Object private properties. - experimental optional array of PrivatePropertyDescriptor privateProperties - # Exception details. - optional ExceptionDetails exceptionDetails - - # Returns all let, const and class variables from global scope. - command globalLexicalScopeNames - parameters - # Specifies in which execution context to lookup global scope variables. - optional ExecutionContextId executionContextId - returns - array of string names - - command queryObjects - parameters - # Identifier of the prototype to return objects for. - RemoteObjectId prototypeObjectId - # Symbolic group name that can be used to release the results. - optional string objectGroup - returns - # Array with objects. - RemoteObject objects - - # Releases remote object with given id. - command releaseObject - parameters - # Identifier of the object to release. - RemoteObjectId objectId - - # Releases all remote objects that belong to a given group. - command releaseObjectGroup - parameters - # Symbolic object group name. - string objectGroup - - # Tells inspected instance to run if it was waiting for debugger to attach. - command runIfWaitingForDebugger - - # Runs script with given id in a given context. - command runScript - parameters - # Id of the script to run. - ScriptId scriptId - # Specifies in which execution context to perform script run. If the parameter is omitted the - # evaluation will be performed in the context of the inspected page. - optional ExecutionContextId executionContextId - # Symbolic group name that can be used to release multiple objects. - optional string objectGroup - # In silent mode exceptions thrown during evaluation are not reported and do not pause - # execution. Overrides `setPauseOnException` state. - optional boolean silent - # Determines whether Command Line API should be available during the evaluation. - optional boolean includeCommandLineAPI - # Whether the result is expected to be a JSON object which should be sent by value. - optional boolean returnByValue - # Whether preview should be generated for the result. - optional boolean generatePreview - # Whether execution should `await` for resulting value and return once awaited promise is - # resolved. - optional boolean awaitPromise - returns - # Run result. - RemoteObject result - # Exception details. - optional ExceptionDetails exceptionDetails - - # Enables or disables async call stacks tracking. - command setAsyncCallStackDepth - redirect Debugger - parameters - # Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async - # call stacks (default). - integer maxDepth - - experimental command setCustomObjectFormatterEnabled - parameters - boolean enabled - - experimental command setMaxCallStackSizeToCapture - parameters - integer size - - # Terminate current or next JavaScript execution. - # Will cancel the termination when the outer-most script execution ends. - experimental command terminateExecution - - # If executionContextId is empty, adds binding with the given name on the - # global objects of all inspected contexts, including those created later, - # bindings survive reloads. - # Binding function takes exactly one argument, this argument should be string, - # in case of any other input, function throws an exception. - # Each binding function call produces Runtime.bindingCalled notification. - experimental command addBinding - parameters - string name - # If specified, the binding would only be exposed to the specified - # execution context. If omitted and `executionContextName` is not set, - # the binding is exposed to all execution contexts of the target. - # This parameter is mutually exclusive with `executionContextName`. - # Deprecated in favor of `executionContextName` due to an unclear use case - # and bugs in implementation (crbug.com/1169639). `executionContextId` will be - # removed in the future. - deprecated optional ExecutionContextId executionContextId - # If specified, the binding is exposed to the executionContext with - # matching name, even for contexts created after the binding is added. - # See also `ExecutionContext.name` and `worldName` parameter to - # `Page.addScriptToEvaluateOnNewDocument`. - # This parameter is mutually exclusive with `executionContextId`. - experimental optional string executionContextName - - # This method does not remove binding function from global object but - # unsubscribes current runtime agent from Runtime.bindingCalled notifications. - experimental command removeBinding - parameters - string name - - # This method tries to lookup and populate exception details for a - # JavaScript Error object. - # Note that the stackTrace portion of the resulting exceptionDetails will - # only be populated if the Runtime domain was enabled at the time when the - # Error was thrown. - experimental command getExceptionDetails - parameters - # The error object for which to resolve the exception details. - RemoteObjectId errorObjectId - returns - optional ExceptionDetails exceptionDetails - - # Notification is issued every time when binding is called. - experimental event bindingCalled - parameters - string name - string payload - # Identifier of the context where the call was made. - ExecutionContextId executionContextId - - # Issued when console API was called. - event consoleAPICalled - parameters - # Type of the call. - enum type - log - debug - info - error - warning - dir - dirxml - table - trace - clear - startGroup - startGroupCollapsed - endGroup - assert - profile - profileEnd - count - timeEnd - # Call arguments. - array of RemoteObject args - # Identifier of the context where the call was made. - ExecutionContextId executionContextId - # Call timestamp. - Timestamp timestamp - # Stack trace captured when the call was made. The async stack chain is automatically reported for - # the following call types: `assert`, `error`, `trace`, `warning`. For other types the async call - # chain can be retrieved using `Debugger.getStackTrace` and `stackTrace.parentId` field. - optional StackTrace stackTrace - # Console context descriptor for calls on non-default console context (not console.*): - # 'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call - # on named context. - experimental optional string context - - # Issued when unhandled exception was revoked. - event exceptionRevoked - parameters - # Reason describing why exception was revoked. - string reason - # The id of revoked exception, as reported in `exceptionThrown`. - integer exceptionId - - # Issued when exception was thrown and unhandled. - event exceptionThrown - parameters - # Timestamp of the exception. - Timestamp timestamp - ExceptionDetails exceptionDetails - - # Issued when new execution context is created. - event executionContextCreated - parameters - # A newly created execution context. - ExecutionContextDescription context - - # Issued when execution context is destroyed. - event executionContextDestroyed - parameters - # Id of the destroyed context - deprecated ExecutionContextId executionContextId - # Unique Id of the destroyed context - experimental string executionContextUniqueId - - # Issued when all executionContexts were cleared in browser - event executionContextsCleared - - # Issued when object should be inspected (for example, as a result of inspect() command line API - # call). - event inspectRequested - parameters - RemoteObject object - object hints - # Identifier of the context where the call was made. - experimental optional ExecutionContextId executionContextId - -# This domain is deprecated. -deprecated domain Schema - - # Description of the protocol domain. - type Domain extends object - properties - # Domain name. - string name - # Domain version. - string version - - # Returns supported domains. - command getDomains - returns - # List of supported domains. - array of Domain domains diff --git a/NativeScript/napi/v8/include/libplatform/DEPS b/NativeScript/napi/v8/include/libplatform/DEPS deleted file mode 100644 index d8bcf998..00000000 --- a/NativeScript/napi/v8/include/libplatform/DEPS +++ /dev/null @@ -1,9 +0,0 @@ -include_rules = [ - "+libplatform/libplatform-export.h", -] - -specific_include_rules = { - "libplatform\.h": [ - "+libplatform/v8-tracing.h", - ], -} diff --git a/NativeScript/napi/v8/include/libplatform/libplatform-export.h b/NativeScript/napi/v8/include/libplatform/libplatform-export.h deleted file mode 100644 index 15618434..00000000 --- a/NativeScript/napi/v8/include/libplatform/libplatform-export.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2016 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_ -#define V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_ - -#if defined(_WIN32) - -#ifdef BUILDING_V8_PLATFORM_SHARED -#define V8_PLATFORM_EXPORT __declspec(dllexport) -#elif USING_V8_PLATFORM_SHARED -#define V8_PLATFORM_EXPORT __declspec(dllimport) -#else -#define V8_PLATFORM_EXPORT -#endif // BUILDING_V8_PLATFORM_SHARED - -#else // defined(_WIN32) - -// Setup for Linux shared library export. -#ifdef BUILDING_V8_PLATFORM_SHARED -#define V8_PLATFORM_EXPORT __attribute__((visibility("default"))) -#else -#define V8_PLATFORM_EXPORT -#endif - -#endif // defined(_WIN32) - -#endif // V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_ diff --git a/NativeScript/napi/v8/include/libplatform/libplatform.h b/NativeScript/napi/v8/include/libplatform/libplatform.h deleted file mode 100644 index 6a34f432..00000000 --- a/NativeScript/napi/v8/include/libplatform/libplatform.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2014 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8_LIBPLATFORM_LIBPLATFORM_H_ -#define V8_LIBPLATFORM_LIBPLATFORM_H_ - -#include - -#include "libplatform/libplatform-export.h" -#include "libplatform/v8-tracing.h" -#include "v8-platform.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { -namespace platform { - -enum class IdleTaskSupport { kDisabled, kEnabled }; -enum class InProcessStackDumping { kDisabled, kEnabled }; - -enum class MessageLoopBehavior : bool { - kDoNotWait = false, - kWaitForWork = true -}; - -enum class PriorityMode : bool { kDontApply, kApply }; - -/** - * Returns a new instance of the default v8::Platform implementation. - * - * The caller will take ownership of the returned pointer. |thread_pool_size| - * is the number of worker threads to allocate for background jobs. If a value - * of zero is passed, a suitable default based on the current number of - * processors online will be chosen. - * If |idle_task_support| is enabled then the platform will accept idle - * tasks (IdleTasksEnabled will return true) and will rely on the embedder - * calling v8::platform::RunIdleTasks to process the idle tasks. - * If |tracing_controller| is nullptr, the default platform will create a - * v8::platform::TracingController instance and use it. - * If |priority_mode| is PriorityMode::kApply, the default platform will use - * multiple task queues executed by threads different system-level priorities - * (where available) to schedule tasks. - */ -V8_PLATFORM_EXPORT std::unique_ptr NewDefaultPlatform( - int thread_pool_size = 0, - IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, - InProcessStackDumping in_process_stack_dumping = - InProcessStackDumping::kDisabled, - std::unique_ptr tracing_controller = {}, - PriorityMode priority_mode = PriorityMode::kDontApply); - -/** - * The same as NewDefaultPlatform but disables the worker thread pool. - * It must be used with the --single-threaded V8 flag. - */ -V8_PLATFORM_EXPORT std::unique_ptr -NewSingleThreadedDefaultPlatform( - IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, - InProcessStackDumping in_process_stack_dumping = - InProcessStackDumping::kDisabled, - std::unique_ptr tracing_controller = {}); - -/** - * Returns a new instance of the default v8::JobHandle implementation. - * - * The job will be executed by spawning up to |num_worker_threads| many worker - * threads on the provided |platform| with the given |priority|. - */ -V8_PLATFORM_EXPORT std::unique_ptr NewDefaultJobHandle( - v8::Platform* platform, v8::TaskPriority priority, - std::unique_ptr job_task, size_t num_worker_threads); - -/** - * Pumps the message loop for the given isolate. - * - * The caller has to make sure that this is called from the right thread. - * Returns true if a task was executed, and false otherwise. If the call to - * PumpMessageLoop is nested within another call to PumpMessageLoop, only - * nestable tasks may run. Otherwise, any task may run. Unless requested through - * the |behavior| parameter, this call does not block if no task is pending. The - * |platform| has to be created using |NewDefaultPlatform|. - */ -V8_PLATFORM_EXPORT bool PumpMessageLoop( - v8::Platform* platform, v8::Isolate* isolate, - MessageLoopBehavior behavior = MessageLoopBehavior::kDoNotWait); - -/** - * Runs pending idle tasks for at most |idle_time_in_seconds| seconds. - * - * The caller has to make sure that this is called from the right thread. - * This call does not block if no task is pending. The |platform| has to be - * created using |NewDefaultPlatform|. - */ -V8_PLATFORM_EXPORT void RunIdleTasks(v8::Platform* platform, - v8::Isolate* isolate, - double idle_time_in_seconds); - -/** - * Notifies the given platform about the Isolate getting deleted soon. Has to be - * called for all Isolates which are deleted - unless we're shutting down the - * platform. - * - * The |platform| has to be created using |NewDefaultPlatform|. - * - */ -V8_PLATFORM_EXPORT void NotifyIsolateShutdown(v8::Platform* platform, - Isolate* isolate); - -} // namespace platform -} // namespace v8 - -#endif // V8_LIBPLATFORM_LIBPLATFORM_H_ diff --git a/NativeScript/napi/v8/include/libplatform/v8-tracing.h b/NativeScript/napi/v8/include/libplatform/v8-tracing.h deleted file mode 100644 index 6039a9c5..00000000 --- a/NativeScript/napi/v8/include/libplatform/v8-tracing.h +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2016 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8_LIBPLATFORM_V8_TRACING_H_ -#define V8_LIBPLATFORM_V8_TRACING_H_ - -#include -#include -#include -#include -#include - -#include "libplatform/libplatform-export.h" -#include "v8-platform.h" // NOLINT(build/include_directory) - -namespace perfetto { -namespace trace_processor { -class TraceProcessorStorage; -} -class TracingSession; -} - -namespace v8 { - -namespace base { -class Mutex; -} // namespace base - -namespace platform { -namespace tracing { - -class TraceEventListener; - -const int kTraceMaxNumArgs = 2; - -class V8_PLATFORM_EXPORT TraceObject { - public: - union ArgValue { - uint64_t as_uint; - int64_t as_int; - double as_double; - const void* as_pointer; - const char* as_string; - }; - - TraceObject() = default; - ~TraceObject(); - void Initialize( - char phase, const uint8_t* category_enabled_flag, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, int num_args, - const char** arg_names, const uint8_t* arg_types, - const uint64_t* arg_values, - std::unique_ptr* arg_convertables, - unsigned int flags, int64_t timestamp, int64_t cpu_timestamp); - void UpdateDuration(int64_t timestamp, int64_t cpu_timestamp); - void InitializeForTesting( - char phase, const uint8_t* category_enabled_flag, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, int num_args, - const char** arg_names, const uint8_t* arg_types, - const uint64_t* arg_values, - std::unique_ptr* arg_convertables, - unsigned int flags, int pid, int tid, int64_t ts, int64_t tts, - uint64_t duration, uint64_t cpu_duration); - - int pid() const { return pid_; } - int tid() const { return tid_; } - char phase() const { return phase_; } - const uint8_t* category_enabled_flag() const { - return category_enabled_flag_; - } - const char* name() const { return name_; } - const char* scope() const { return scope_; } - uint64_t id() const { return id_; } - uint64_t bind_id() const { return bind_id_; } - int num_args() const { return num_args_; } - const char** arg_names() { return arg_names_; } - uint8_t* arg_types() { return arg_types_; } - ArgValue* arg_values() { return arg_values_; } - std::unique_ptr* arg_convertables() { - return arg_convertables_; - } - unsigned int flags() const { return flags_; } - int64_t ts() { return ts_; } - int64_t tts() { return tts_; } - uint64_t duration() { return duration_; } - uint64_t cpu_duration() { return cpu_duration_; } - - private: - int pid_; - int tid_; - char phase_; - const char* name_; - const char* scope_; - const uint8_t* category_enabled_flag_; - uint64_t id_; - uint64_t bind_id_; - int num_args_ = 0; - const char* arg_names_[kTraceMaxNumArgs]; - uint8_t arg_types_[kTraceMaxNumArgs]; - ArgValue arg_values_[kTraceMaxNumArgs]; - std::unique_ptr - arg_convertables_[kTraceMaxNumArgs]; - char* parameter_copy_storage_ = nullptr; - unsigned int flags_; - int64_t ts_; - int64_t tts_; - uint64_t duration_; - uint64_t cpu_duration_; - - // Disallow copy and assign - TraceObject(const TraceObject&) = delete; - void operator=(const TraceObject&) = delete; -}; - -class V8_PLATFORM_EXPORT TraceWriter { - public: - TraceWriter() = default; - virtual ~TraceWriter() = default; - virtual void AppendTraceEvent(TraceObject* trace_event) = 0; - virtual void Flush() = 0; - - static TraceWriter* CreateJSONTraceWriter(std::ostream& stream); - static TraceWriter* CreateJSONTraceWriter(std::ostream& stream, - const std::string& tag); - - static TraceWriter* CreateSystemInstrumentationTraceWriter(); - - private: - // Disallow copy and assign - TraceWriter(const TraceWriter&) = delete; - void operator=(const TraceWriter&) = delete; -}; - -class V8_PLATFORM_EXPORT TraceBufferChunk { - public: - explicit TraceBufferChunk(uint32_t seq); - - void Reset(uint32_t new_seq); - bool IsFull() const { return next_free_ == kChunkSize; } - TraceObject* AddTraceEvent(size_t* event_index); - TraceObject* GetEventAt(size_t index) { return &chunk_[index]; } - - uint32_t seq() const { return seq_; } - size_t size() const { return next_free_; } - - static const size_t kChunkSize = 64; - - private: - size_t next_free_ = 0; - TraceObject chunk_[kChunkSize]; - uint32_t seq_; - - // Disallow copy and assign - TraceBufferChunk(const TraceBufferChunk&) = delete; - void operator=(const TraceBufferChunk&) = delete; -}; - -class V8_PLATFORM_EXPORT TraceBuffer { - public: - TraceBuffer() = default; - virtual ~TraceBuffer() = default; - - virtual TraceObject* AddTraceEvent(uint64_t* handle) = 0; - virtual TraceObject* GetEventByHandle(uint64_t handle) = 0; - virtual bool Flush() = 0; - - static const size_t kRingBufferChunks = 1024; - - static TraceBuffer* CreateTraceBufferRingBuffer(size_t max_chunks, - TraceWriter* trace_writer); - - private: - // Disallow copy and assign - TraceBuffer(const TraceBuffer&) = delete; - void operator=(const TraceBuffer&) = delete; -}; - -// Options determines how the trace buffer stores data. -enum TraceRecordMode { - // Record until the trace buffer is full. - RECORD_UNTIL_FULL, - - // Record until the user ends the trace. The trace buffer is a fixed size - // and we use it as a ring buffer during recording. - RECORD_CONTINUOUSLY, - - // Record until the trace buffer is full, but with a huge buffer size. - RECORD_AS_MUCH_AS_POSSIBLE, - - // Echo to console. Events are discarded. - ECHO_TO_CONSOLE, -}; - -class V8_PLATFORM_EXPORT TraceConfig { - public: - typedef std::vector StringList; - - static TraceConfig* CreateDefaultTraceConfig(); - - TraceConfig() : enable_systrace_(false), enable_argument_filter_(false) {} - TraceRecordMode GetTraceRecordMode() const { return record_mode_; } - const StringList& GetEnabledCategories() const { - return included_categories_; - } - bool IsSystraceEnabled() const { return enable_systrace_; } - bool IsArgumentFilterEnabled() const { return enable_argument_filter_; } - - void SetTraceRecordMode(TraceRecordMode mode) { record_mode_ = mode; } - void EnableSystrace() { enable_systrace_ = true; } - void EnableArgumentFilter() { enable_argument_filter_ = true; } - - void AddIncludedCategory(const char* included_category); - - bool IsCategoryGroupEnabled(const char* category_group) const; - - private: - TraceRecordMode record_mode_; - bool enable_systrace_ : 1; - bool enable_argument_filter_ : 1; - StringList included_categories_; - - // Disallow copy and assign - TraceConfig(const TraceConfig&) = delete; - void operator=(const TraceConfig&) = delete; -}; - -#if defined(_MSC_VER) -#define V8_PLATFORM_NON_EXPORTED_BASE(code) \ - __pragma(warning(suppress : 4275)) code -#else -#define V8_PLATFORM_NON_EXPORTED_BASE(code) code -#endif // defined(_MSC_VER) - -class V8_PLATFORM_EXPORT TracingController - : public V8_PLATFORM_NON_EXPORTED_BASE(v8::TracingController) { - public: - TracingController(); - ~TracingController() override; - -#if defined(V8_USE_PERFETTO) - // Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides - // the output stream for the JSON trace data. - void InitializeForPerfetto(std::ostream* output_stream); - // Provide an optional listener for testing that will receive trace events. - // Must be called before StartTracing(). - void SetTraceEventListenerForTesting(TraceEventListener* listener); -#else // defined(V8_USE_PERFETTO) - // The pointer returned from GetCategoryGroupEnabled() points to a value with - // zero or more of the following bits. Used in this class only. The - // TRACE_EVENT macros should only use the value as a bool. These values must - // be in sync with macro values in TraceEvent.h in Blink. - enum CategoryGroupEnabledFlags { - // Category group enabled for the recording mode. - ENABLED_FOR_RECORDING = 1 << 0, - // Category group enabled by SetEventCallbackEnabled(). - ENABLED_FOR_EVENT_CALLBACK = 1 << 2, - // Category group enabled to export events to ETW. - ENABLED_FOR_ETW_EXPORT = 1 << 3 - }; - - // Takes ownership of |trace_buffer|. - void Initialize(TraceBuffer* trace_buffer); - - // v8::TracingController implementation. - const uint8_t* GetCategoryGroupEnabled(const char* category_group) override; - uint64_t AddTraceEvent( - char phase, const uint8_t* category_enabled_flag, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, - const char** arg_names, const uint8_t* arg_types, - const uint64_t* arg_values, - std::unique_ptr* arg_convertables, - unsigned int flags) override; - uint64_t AddTraceEventWithTimestamp( - char phase, const uint8_t* category_enabled_flag, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, - const char** arg_names, const uint8_t* arg_types, - const uint64_t* arg_values, - std::unique_ptr* arg_convertables, - unsigned int flags, int64_t timestamp) override; - void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, - const char* name, uint64_t handle) override; - - static const char* GetCategoryGroupName(const uint8_t* category_enabled_flag); - - void AddTraceStateObserver( - v8::TracingController::TraceStateObserver* observer) override; - void RemoveTraceStateObserver( - v8::TracingController::TraceStateObserver* observer) override; -#endif // !defined(V8_USE_PERFETTO) - - void StartTracing(TraceConfig* trace_config); - void StopTracing(); - - protected: -#if !defined(V8_USE_PERFETTO) - virtual int64_t CurrentTimestampMicroseconds(); - virtual int64_t CurrentCpuTimestampMicroseconds(); -#endif // !defined(V8_USE_PERFETTO) - - private: -#if !defined(V8_USE_PERFETTO) - void UpdateCategoryGroupEnabledFlag(size_t category_index); - void UpdateCategoryGroupEnabledFlags(); -#endif // !defined(V8_USE_PERFETTO) - - std::unique_ptr mutex_; - std::unique_ptr trace_config_; - std::atomic_bool recording_{false}; - -#if defined(V8_USE_PERFETTO) - std::ostream* output_stream_ = nullptr; - std::unique_ptr - trace_processor_; - TraceEventListener* listener_for_testing_ = nullptr; - std::unique_ptr tracing_session_; -#else // !defined(V8_USE_PERFETTO) - std::unordered_set observers_; - std::unique_ptr trace_buffer_; -#endif // !defined(V8_USE_PERFETTO) - - // Disallow copy and assign - TracingController(const TracingController&) = delete; - void operator=(const TracingController&) = delete; -}; - -#undef V8_PLATFORM_NON_EXPORTED_BASE - -} // namespace tracing -} // namespace platform -} // namespace v8 - -#endif // V8_LIBPLATFORM_V8_TRACING_H_ diff --git a/NativeScript/napi/v8/include/v8-array-buffer.h b/NativeScript/napi/v8/include/v8-array-buffer.h deleted file mode 100644 index 804fc42c..00000000 --- a/NativeScript/napi/v8/include/v8-array-buffer.h +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_ARRAY_BUFFER_H_ -#define INCLUDE_V8_ARRAY_BUFFER_H_ - -#include - -#include - -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-object.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class SharedArrayBuffer; - -#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT -// The number of required internal fields can be defined by embedder. -#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2 -#endif - -enum class ArrayBufferCreationMode { kInternalized, kExternalized }; - -/** - * A wrapper around the backing store (i.e. the raw memory) of an array buffer. - * See a document linked in http://crbug.com/v8/9908 for more information. - * - * The allocation and destruction of backing stores is generally managed by - * V8. Clients should always use standard C++ memory ownership types (i.e. - * std::unique_ptr and std::shared_ptr) to manage lifetimes of backing stores - * properly, since V8 internal objects may alias backing stores. - * - * This object does not keep the underlying |ArrayBuffer::Allocator| alive by - * default. Use Isolate::CreateParams::array_buffer_allocator_shared when - * creating the Isolate to make it hold a reference to the allocator itself. - */ -class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase { - public: - ~BackingStore(); - - /** - * Return a pointer to the beginning of the memory block for this backing - * store. The pointer is only valid as long as this backing store object - * lives. - */ - void* Data() const; - - /** - * The length (in bytes) of this backing store. - */ - size_t ByteLength() const; - - /** - * The maximum length (in bytes) that this backing store may grow to. - * - * If this backing store was created for a resizable ArrayBuffer or a growable - * SharedArrayBuffer, it is >= ByteLength(). Otherwise it is == - * ByteLength(). - */ - size_t MaxByteLength() const; - - /** - * Indicates whether the backing store was created for an ArrayBuffer or - * a SharedArrayBuffer. - */ - bool IsShared() const; - - /** - * Indicates whether the backing store was created for a resizable ArrayBuffer - * or a growable SharedArrayBuffer, and thus may be resized by user JavaScript - * code. - */ - bool IsResizableByUserJavaScript() const; - - /** - * Prevent implicit instantiation of operator delete with size_t argument. - * The size_t argument would be incorrect because ptr points to the - * internal BackingStore object. - */ - void operator delete(void* ptr) { ::operator delete(ptr); } - - /** - * Wrapper around ArrayBuffer::Allocator::Reallocate that preserves IsShared. - * Assumes that the backing_store was allocated by the ArrayBuffer allocator - * of the given isolate. - */ - static std::unique_ptr Reallocate( - v8::Isolate* isolate, std::unique_ptr backing_store, - size_t byte_length); - - /** - * This callback is used only if the memory block for a BackingStore cannot be - * allocated with an ArrayBuffer::Allocator. In such cases the destructor of - * the BackingStore invokes the callback to free the memory block. - */ - using DeleterCallback = void (*)(void* data, size_t length, - void* deleter_data); - - /** - * If the memory block of a BackingStore is static or is managed manually, - * then this empty deleter along with nullptr deleter_data can be passed to - * ArrayBuffer::NewBackingStore to indicate that. - * - * The manually managed case should be used with caution and only when it - * is guaranteed that the memory block freeing happens after detaching its - * ArrayBuffer. - */ - static void EmptyDeleter(void* data, size_t length, void* deleter_data); - - private: - /** - * See [Shared]ArrayBuffer::GetBackingStore and - * [Shared]ArrayBuffer::NewBackingStore. - */ - BackingStore(); -}; - -#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) -// Use v8::BackingStore::DeleterCallback instead. -using BackingStoreDeleterCallback = void (*)(void* data, size_t length, - void* deleter_data); - -#endif - -/** - * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5). - */ -class V8_EXPORT ArrayBuffer : public Object { - public: - /** - * A thread-safe allocator that V8 uses to allocate |ArrayBuffer|'s memory. - * The allocator is a global V8 setting. It has to be set via - * Isolate::CreateParams. - * - * Memory allocated through this allocator by V8 is accounted for as external - * memory by V8. Note that V8 keeps track of the memory for all internalized - * |ArrayBuffer|s. Responsibility for tracking external memory (using - * Isolate::AdjustAmountOfExternalAllocatedMemory) is handed over to the - * embedder upon externalization and taken over upon internalization (creating - * an internalized buffer from an existing buffer). - * - * Note that it is unsafe to call back into V8 from any of the allocator - * functions. - */ - class V8_EXPORT Allocator { - public: - virtual ~Allocator() = default; - - /** - * Allocate |length| bytes. Return nullptr if allocation is not successful. - * Memory should be initialized to zeroes. - */ - virtual void* Allocate(size_t length) = 0; - - /** - * Allocate |length| bytes. Return nullptr if allocation is not successful. - * Memory does not have to be initialized. - */ - virtual void* AllocateUninitialized(size_t length) = 0; - - /** - * Free the memory block of size |length|, pointed to by |data|. - * That memory is guaranteed to be previously allocated by |Allocate|. - */ - virtual void Free(void* data, size_t length) = 0; - - /** - * Reallocate the memory block of size |old_length| to a memory block of - * size |new_length| by expanding, contracting, or copying the existing - * memory block. If |new_length| > |old_length|, then the new part of - * the memory must be initialized to zeros. Return nullptr if reallocation - * is not successful. - * - * The caller guarantees that the memory block was previously allocated - * using Allocate or AllocateUninitialized. - * - * The default implementation allocates a new block and copies data. - */ - virtual void* Reallocate(void* data, size_t old_length, size_t new_length); - - /** - * ArrayBuffer allocation mode. kNormal is a malloc/free style allocation, - * while kReservation is for larger allocations with the ability to set - * access permissions. - */ - enum class AllocationMode { kNormal, kReservation }; - - /** - * Convenience allocator. - * - * When the sandbox is enabled, this allocator will allocate its backing - * memory inside the sandbox. Otherwise, it will rely on malloc/free. - * - * Caller takes ownership, i.e. the returned object needs to be freed using - * |delete allocator| once it is no longer in use. - */ - static Allocator* NewDefaultAllocator(); - }; - - /** - * Data length in bytes. - */ - size_t ByteLength() const; - - /** - * Maximum length in bytes. - */ - size_t MaxByteLength() const; - - /** - * Create a new ArrayBuffer. Allocate |byte_length| bytes. - * Allocated memory will be owned by a created ArrayBuffer and - * will be deallocated when it is garbage-collected, - * unless the object is externalized. - */ - static Local New(Isolate* isolate, size_t byte_length); - - /** - * Create a new ArrayBuffer with an existing backing store. - * The created array keeps a reference to the backing store until the array - * is garbage collected. Note that the IsExternal bit does not affect this - * reference from the array to the backing store. - * - * In future IsExternal bit will be removed. Until then the bit is set as - * follows. If the backing store does not own the underlying buffer, then - * the array is created in externalized state. Otherwise, the array is created - * in internalized state. In the latter case the array can be transitioned - * to the externalized state using Externalize(backing_store). - */ - static Local New(Isolate* isolate, - std::shared_ptr backing_store); - - /** - * Returns a new standalone BackingStore that is allocated using the array - * buffer allocator of the isolate. The result can be later passed to - * ArrayBuffer::New. - * - * If the allocator returns nullptr, then the function may cause GCs in the - * given isolate and re-try the allocation. If GCs do not help, then the - * function will crash with an out-of-memory error. - */ - static std::unique_ptr NewBackingStore(Isolate* isolate, - size_t byte_length); - /** - * Returns a new standalone BackingStore that takes over the ownership of - * the given buffer. The destructor of the BackingStore invokes the given - * deleter callback. - * - * The result can be later passed to ArrayBuffer::New. The raw pointer - * to the buffer must not be passed again to any V8 API function. - */ - static std::unique_ptr NewBackingStore( - void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, - void* deleter_data); - - /** - * Returns a new resizable standalone BackingStore that is allocated using the - * array buffer allocator of the isolate. The result can be later passed to - * ArrayBuffer::New. - * - * |byte_length| must be <= |max_byte_length|. - * - * This function is usable without an isolate. Unlike |NewBackingStore| calls - * with an isolate, GCs cannot be triggered, and there are no - * retries. Allocation failure will cause the function to crash with an - * out-of-memory error. - */ - static std::unique_ptr NewResizableBackingStore( - size_t byte_length, size_t max_byte_length); - - /** - * Returns true if this ArrayBuffer may be detached. - */ - bool IsDetachable() const; - - /** - * Returns true if this ArrayBuffer has been detached. - */ - bool WasDetached() const; - - /** - * Detaches this ArrayBuffer and all its views (typed arrays). - * Detaching sets the byte length of the buffer and all typed arrays to zero, - * preventing JavaScript from ever accessing underlying backing store. - * ArrayBuffer should have been externalized and must be detachable. - */ - V8_DEPRECATE_SOON( - "Use the version which takes a key parameter (passing a null handle is " - "ok).") - void Detach(); - - /** - * Detaches this ArrayBuffer and all its views (typed arrays). - * Detaching sets the byte length of the buffer and all typed arrays to zero, - * preventing JavaScript from ever accessing underlying backing store. - * ArrayBuffer should have been externalized and must be detachable. Returns - * Nothing if the key didn't pass the [[ArrayBufferDetachKey]] check, - * Just(true) otherwise. - */ - V8_WARN_UNUSED_RESULT Maybe Detach(v8::Local key); - - /** - * Sets the ArrayBufferDetachKey. - */ - void SetDetachKey(v8::Local key); - - /** - * Get a shared pointer to the backing store of this array buffer. This - * pointer coordinates the lifetime management of the internal storage - * with any live ArrayBuffers on the heap, even across isolates. The embedder - * should not attempt to manage lifetime of the storage through other means. - * - * The returned shared pointer will not be empty, even if the ArrayBuffer has - * been detached. Use |WasDetached| to tell if it has been detached instead. - */ - std::shared_ptr GetBackingStore(); - - /** - * More efficient shortcut for GetBackingStore()->Data(). The returned pointer - * is valid as long as the ArrayBuffer is alive. - */ - void* Data() const; - - V8_INLINE static ArrayBuffer* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; - static const int kEmbedderFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; - - private: - ArrayBuffer(); - static void CheckCast(Value* obj); -}; - -#ifndef V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT -// The number of required internal fields can be defined by embedder. -#define V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT 2 -#endif - -/** - * A base class for an instance of one of "views" over ArrayBuffer, - * including TypedArrays and DataView (ES6 draft 15.13). - */ -class V8_EXPORT ArrayBufferView : public Object { - public: - /** - * Returns underlying ArrayBuffer. - */ - Local Buffer(); - /** - * Byte offset in |Buffer|. - */ - size_t ByteOffset(); - /** - * Size of a view in bytes. - */ - size_t ByteLength(); - - /** - * Copy the contents of the ArrayBufferView's buffer to an embedder defined - * memory without additional overhead that calling ArrayBufferView::Buffer - * might incur. - * - * Will write at most min(|byte_length|, ByteLength) bytes starting at - * ByteOffset of the underlying buffer to the memory starting at |dest|. - * Returns the number of bytes actually written. - */ - size_t CopyContents(void* dest, size_t byte_length); - - /** - * Returns true if ArrayBufferView's backing ArrayBuffer has already been - * allocated. - */ - bool HasBuffer() const; - - V8_INLINE static ArrayBufferView* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - static const int kInternalFieldCount = - V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT; - static const int kEmbedderFieldCount = - V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT; - - private: - ArrayBufferView(); - static void CheckCast(Value* obj); -}; - -/** - * An instance of DataView constructor (ES6 draft 15.13.7). - */ -class V8_EXPORT DataView : public ArrayBufferView { - public: - static Local New(Local array_buffer, - size_t byte_offset, size_t length); - static Local New(Local shared_array_buffer, - size_t byte_offset, size_t length); - V8_INLINE static DataView* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - private: - DataView(); - static void CheckCast(Value* obj); -}; - -/** - * An instance of the built-in SharedArrayBuffer constructor. - */ -class V8_EXPORT SharedArrayBuffer : public Object { - public: - /** - * Data length in bytes. - */ - size_t ByteLength() const; - - /** - * Maximum length in bytes. - */ - size_t MaxByteLength() const; - - /** - * Create a new SharedArrayBuffer. Allocate |byte_length| bytes. - * Allocated memory will be owned by a created SharedArrayBuffer and - * will be deallocated when it is garbage-collected, - * unless the object is externalized. - */ - static Local New(Isolate* isolate, size_t byte_length); - - /** - * Create a new SharedArrayBuffer with an existing backing store. - * The created array keeps a reference to the backing store until the array - * is garbage collected. Note that the IsExternal bit does not affect this - * reference from the array to the backing store. - * - * In future IsExternal bit will be removed. Until then the bit is set as - * follows. If the backing store does not own the underlying buffer, then - * the array is created in externalized state. Otherwise, the array is created - * in internalized state. In the latter case the array can be transitioned - * to the externalized state using Externalize(backing_store). - */ - static Local New( - Isolate* isolate, std::shared_ptr backing_store); - - /** - * Returns a new standalone BackingStore that is allocated using the array - * buffer allocator of the isolate. The result can be later passed to - * SharedArrayBuffer::New. - * - * If the allocator returns nullptr, then the function may cause GCs in the - * given isolate and re-try the allocation. If GCs do not help, then the - * function will crash with an out-of-memory error. - */ - static std::unique_ptr NewBackingStore(Isolate* isolate, - size_t byte_length); - /** - * Returns a new standalone BackingStore that takes over the ownership of - * the given buffer. The destructor of the BackingStore invokes the given - * deleter callback. - * - * The result can be later passed to SharedArrayBuffer::New. The raw pointer - * to the buffer must not be passed again to any V8 functions. - */ - static std::unique_ptr NewBackingStore( - void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, - void* deleter_data); - - /** - * Get a shared pointer to the backing store of this array buffer. This - * pointer coordinates the lifetime management of the internal storage - * with any live ArrayBuffers on the heap, even across isolates. The embedder - * should not attempt to manage lifetime of the storage through other means. - */ - std::shared_ptr GetBackingStore(); - - /** - * More efficient shortcut for GetBackingStore()->Data(). The returned pointer - * is valid as long as the ArrayBuffer is alive. - */ - void* Data() const; - - V8_INLINE static SharedArrayBuffer* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; - - private: - SharedArrayBuffer(); - static void CheckCast(Value* obj); -}; - -} // namespace v8 - -#endif // INCLUDE_V8_ARRAY_BUFFER_H_ diff --git a/NativeScript/napi/v8/include/v8-callbacks.h b/NativeScript/napi/v8/include/v8-callbacks.h deleted file mode 100644 index 2a25b9ee..00000000 --- a/NativeScript/napi/v8/include/v8-callbacks.h +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_ISOLATE_CALLBACKS_H_ -#define INCLUDE_V8_ISOLATE_CALLBACKS_H_ - -#include - -#include -#include - -#include "cppgc/common.h" -#include "v8-data.h" // NOLINT(build/include_directory) -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-promise.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -#if defined(V8_OS_WIN) -struct _EXCEPTION_POINTERS; -#endif - -namespace v8 { - -template -class FunctionCallbackInfo; -class Isolate; -class Message; -class Module; -class Object; -class Promise; -class ScriptOrModule; -class String; -class UnboundScript; -class Value; - -/** - * A JIT code event is issued each time code is added, moved or removed. - * - * \note removal events are not currently issued. - */ -struct JitCodeEvent { - enum EventType { - CODE_ADDED, - CODE_MOVED, - CODE_REMOVED, - CODE_ADD_LINE_POS_INFO, - CODE_START_LINE_INFO_RECORDING, - CODE_END_LINE_INFO_RECORDING - }; - // Definition of the code position type. The "POSITION" type means the place - // in the source code which are of interest when making stack traces to - // pin-point the source location of a stack frame as close as possible. - // The "STATEMENT_POSITION" means the place at the beginning of each - // statement, and is used to indicate possible break locations. - enum PositionType { POSITION, STATEMENT_POSITION }; - - // There are three different kinds of CodeType, one for JIT code generated - // by the optimizing compiler, one for byte code generated for the - // interpreter, and one for code generated from Wasm. For JIT_CODE and - // WASM_CODE, |code_start| points to the beginning of jitted assembly code, - // while for BYTE_CODE events, |code_start| points to the first bytecode of - // the interpreted function. - enum CodeType { BYTE_CODE, JIT_CODE, WASM_CODE }; - - // Type of event. - EventType type; - CodeType code_type; - // Start of the instructions. - void* code_start; - // Size of the instructions. - size_t code_len; - // Script info for CODE_ADDED event. - Local script; - // User-defined data for *_LINE_INFO_* event. It's used to hold the source - // code line information which is returned from the - // CODE_START_LINE_INFO_RECORDING event. And it's passed to subsequent - // CODE_ADD_LINE_POS_INFO and CODE_END_LINE_INFO_RECORDING events. - void* user_data; - - struct name_t { - // Name of the object associated with the code, note that the string is not - // zero-terminated. - const char* str; - // Number of chars in str. - size_t len; - }; - - struct line_info_t { - // PC offset - size_t offset; - // Code position - size_t pos; - // The position type. - PositionType position_type; - }; - - struct wasm_source_info_t { - // Source file name. - const char* filename; - // Length of filename. - size_t filename_size; - // Line number table, which maps offsets of JITted code to line numbers of - // source file. - const line_info_t* line_number_table; - // Number of entries in the line number table. - size_t line_number_table_size; - }; - - wasm_source_info_t* wasm_source_info = nullptr; - - union { - // Only valid for CODE_ADDED. - struct name_t name; - - // Only valid for CODE_ADD_LINE_POS_INFO - struct line_info_t line_info; - - // New location of instructions. Only valid for CODE_MOVED. - void* new_code_start; - }; - - Isolate* isolate; -}; - -/** - * Option flags passed to the SetJitCodeEventHandler function. - */ -enum JitCodeEventOptions { - kJitCodeEventDefault = 0, - // Generate callbacks for already existent code. - kJitCodeEventEnumExisting = 1 -}; - -/** - * Callback function passed to SetJitCodeEventHandler. - * - * \param event code add, move or removal event. - */ -using JitCodeEventHandler = void (*)(const JitCodeEvent* event); - -// --- Garbage Collection Callbacks --- - -/** - * Applications can register callback functions which will be called before and - * after certain garbage collection operations. Allocations are not allowed in - * the callback functions, you therefore cannot manipulate objects (set or - * delete properties for example) since it is possible such operations will - * result in the allocation of objects. - * TODO(v8:12612): Deprecate kGCTypeMinorMarkSweep after updating blink. - */ -enum GCType { - kGCTypeScavenge = 1 << 0, - kGCTypeMinorMarkSweep = 1 << 1, - kGCTypeMinorMarkCompact V8_DEPRECATE_SOON( - "Use kGCTypeMinorMarkSweep instead of kGCTypeMinorMarkCompact.") = - kGCTypeMinorMarkSweep, - kGCTypeMarkSweepCompact = 1 << 2, - kGCTypeIncrementalMarking = 1 << 3, - kGCTypeProcessWeakCallbacks = 1 << 4, - kGCTypeAll = kGCTypeScavenge | kGCTypeMinorMarkSweep | - kGCTypeMarkSweepCompact | kGCTypeIncrementalMarking | - kGCTypeProcessWeakCallbacks -}; - -/** - * GCCallbackFlags is used to notify additional information about the GC - * callback. - * - kGCCallbackFlagConstructRetainedObjectInfos: The GC callback is for - * constructing retained object infos. - * - kGCCallbackFlagForced: The GC callback is for a forced GC for testing. - * - kGCCallbackFlagSynchronousPhantomCallbackProcessing: The GC callback - * is called synchronously without getting posted to an idle task. - * - kGCCallbackFlagCollectAllAvailableGarbage: The GC callback is called - * in a phase where V8 is trying to collect all available garbage - * (e.g., handling a low memory notification). - * - kGCCallbackScheduleIdleGarbageCollection: The GC callback is called to - * trigger an idle garbage collection. - */ -enum GCCallbackFlags { - kNoGCCallbackFlags = 0, - kGCCallbackFlagConstructRetainedObjectInfos = 1 << 1, - kGCCallbackFlagForced = 1 << 2, - kGCCallbackFlagSynchronousPhantomCallbackProcessing = 1 << 3, - kGCCallbackFlagCollectAllAvailableGarbage = 1 << 4, - kGCCallbackFlagCollectAllExternalMemory = 1 << 5, - kGCCallbackScheduleIdleGarbageCollection = 1 << 6, -}; - -using GCCallback = void (*)(GCType type, GCCallbackFlags flags); - -using InterruptCallback = void (*)(Isolate* isolate, void* data); - -/** - * This callback is invoked when the heap size is close to the heap limit and - * V8 is likely to abort with out-of-memory error. - * The callback can extend the heap limit by returning a value that is greater - * than the current_heap_limit. The initial heap limit is the limit that was - * set after heap setup. - */ -using NearHeapLimitCallback = size_t (*)(void* data, size_t current_heap_limit, - size_t initial_heap_limit); - -/** - * Callback function passed to SetUnhandledExceptionCallback. - */ -#if defined(V8_OS_WIN) -using UnhandledExceptionCallback = - int (*)(_EXCEPTION_POINTERS* exception_pointers); -#endif - -// --- Counters Callbacks --- - -using CounterLookupCallback = int* (*)(const char* name); - -using CreateHistogramCallback = void* (*)(const char* name, int min, int max, - size_t buckets); - -using AddHistogramSampleCallback = void (*)(void* histogram, int sample); - -// --- Exceptions --- - -using FatalErrorCallback = void (*)(const char* location, const char* message); - -struct OOMDetails { - bool is_heap_oom = false; - const char* detail = nullptr; -}; - -using OOMErrorCallback = void (*)(const char* location, - const OOMDetails& details); - -using MessageCallback = void (*)(Local message, Local data); - -// --- Tracing --- - -enum LogEventStatus : int { kStart = 0, kEnd = 1, kStamp = 2 }; -using LogEventCallback = void (*)(const char* name, - int /* LogEventStatus */ status); - -// --- Crashkeys Callback --- -enum class CrashKeyId { - kIsolateAddress, - kReadonlySpaceFirstPageAddress, - kMapSpaceFirstPageAddress V8_ENUM_DEPRECATE_SOON("Map space got removed"), - kOldSpaceFirstPageAddress, - kCodeRangeBaseAddress, - kCodeSpaceFirstPageAddress, - kDumpType, - kSnapshotChecksumCalculated, - kSnapshotChecksumExpected, -}; - -using AddCrashKeyCallback = void (*)(CrashKeyId id, const std::string& value); - -// --- Enter/Leave Script Callback --- -using BeforeCallEnteredCallback = void (*)(Isolate*); -using CallCompletedCallback = void (*)(Isolate*); - -// --- AllowCodeGenerationFromStrings callbacks --- - -/** - * Callback to check if code generation from strings is allowed. See - * Context::AllowCodeGenerationFromStrings. - */ -using AllowCodeGenerationFromStringsCallback = bool (*)(Local context, - Local source); - -struct ModifyCodeGenerationFromStringsResult { - // If true, proceed with the codegen algorithm. Otherwise, block it. - bool codegen_allowed = false; - // Overwrite the original source with this string, if present. - // Use the original source if empty. - // This field is considered only if codegen_allowed is true. - MaybeLocal modified_source; -}; - -/** - * Access type specification. - */ -enum AccessType { - ACCESS_GET, - ACCESS_SET, - ACCESS_HAS, - ACCESS_DELETE, - ACCESS_KEYS -}; - -// --- Failed Access Check Callback --- - -using FailedAccessCheckCallback = void (*)(Local target, - AccessType type, Local data); - -/** - * Callback to check if codegen is allowed from a source object, and convert - * the source to string if necessary. See: ModifyCodeGenerationFromStrings. - */ -using ModifyCodeGenerationFromStringsCallback = - ModifyCodeGenerationFromStringsResult (*)(Local context, - Local source); -using ModifyCodeGenerationFromStringsCallback2 = - ModifyCodeGenerationFromStringsResult (*)(Local context, - Local source, - bool is_code_like); - -// --- WebAssembly compilation callbacks --- -using ExtensionCallback = bool (*)(const FunctionCallbackInfo&); - -using AllowWasmCodeGenerationCallback = bool (*)(Local context, - Local source); - -// --- Callback for APIs defined on v8-supported objects, but implemented -// by the embedder. Example: WebAssembly.{compile|instantiate}Streaming --- -using ApiImplementationCallback = void (*)(const FunctionCallbackInfo&); - -// --- Callback for WebAssembly.compileStreaming --- -using WasmStreamingCallback = void (*)(const FunctionCallbackInfo&); - -enum class WasmAsyncSuccess { kSuccess, kFail }; - -// --- Callback called when async WebAssembly operations finish --- -using WasmAsyncResolvePromiseCallback = void (*)( - Isolate* isolate, Local context, Local resolver, - Local result, WasmAsyncSuccess success); - -// --- Callback for loading source map file for Wasm profiling support -using WasmLoadSourceMapCallback = Local (*)(Isolate* isolate, - const char* name); - -// --- Callback for checking if WebAssembly GC is enabled --- -// If the callback returns true, it will also enable Wasm stringrefs. -using WasmGCEnabledCallback = bool (*)(Local context); - -// --- Callback for checking if WebAssembly imported strings are enabled --- -using WasmImportedStringsEnabledCallback = bool (*)(Local context); - -// --- Callback for checking if the SharedArrayBuffer constructor is enabled --- -using SharedArrayBufferConstructorEnabledCallback = - bool (*)(Local context); - -// --- Callback for checking if the compile hints magic comments are enabled --- -using JavaScriptCompileHintsMagicEnabledCallback = - bool (*)(Local context); - -/** - * HostImportModuleDynamicallyCallback is called when we - * require the embedder to load a module. This is used as part of the dynamic - * import syntax. - * - * The referrer contains metadata about the script/module that calls - * import. - * - * The specifier is the name of the module that should be imported. - * - * The import_assertions are import assertions for this request in the form: - * [key1, value1, key2, value2, ...] where the keys and values are of type - * v8::String. Note, unlike the FixedArray passed to ResolveModuleCallback and - * returned from ModuleRequest::GetImportAssertions(), this array does not - * contain the source Locations of the assertions. - * - * The embedder must compile, instantiate, evaluate the Module, and - * obtain its namespace object. - * - * The Promise returned from this function is forwarded to userland - * JavaScript. The embedder must resolve this promise with the module - * namespace object. In case of an exception, the embedder must reject - * this promise with the exception. If the promise creation itself - * fails (e.g. due to stack overflow), the embedder must propagate - * that exception by returning an empty MaybeLocal. - */ -using HostImportModuleDynamicallyWithImportAssertionsCallback = - MaybeLocal (*)(Local context, - Local referrer, - Local specifier, - Local import_assertions); -using HostImportModuleDynamicallyCallback = MaybeLocal (*)( - Local context, Local host_defined_options, - Local resource_name, Local specifier, - Local import_assertions); - -/** - * Callback for requesting a compile hint for a function from the embedder. The - * first parameter is the position of the function in source code and the second - * parameter is embedder data to be passed back. - */ -using CompileHintCallback = bool (*)(int, void*); - -/** - * HostInitializeImportMetaObjectCallback is called the first time import.meta - * is accessed for a module. Subsequent access will reuse the same value. - * - * The method combines two implementation-defined abstract operations into one: - * HostGetImportMetaProperties and HostFinalizeImportMeta. - * - * The embedder should use v8::Object::CreateDataProperty to add properties on - * the meta object. - */ -using HostInitializeImportMetaObjectCallback = void (*)(Local context, - Local module, - Local meta); - -/** - * HostCreateShadowRealmContextCallback is called each time a ShadowRealm is - * being constructed in the initiator_context. - * - * The method combines Context creation and implementation defined abstract - * operation HostInitializeShadowRealm into one. - * - * The embedder should use v8::Context::New or v8::Context:NewFromSnapshot to - * create a new context. If the creation fails, the embedder must propagate - * that exception by returning an empty MaybeLocal. - */ -using HostCreateShadowRealmContextCallback = - MaybeLocal (*)(Local initiator_context); - -/** - * PrepareStackTraceCallback is called when the stack property of an error is - * first accessed. The return value will be used as the stack value. If this - * callback is registed, the |Error.prepareStackTrace| API will be disabled. - * |sites| is an array of call sites, specified in - * https://v8.dev/docs/stack-trace-api - */ -using PrepareStackTraceCallback = MaybeLocal (*)(Local context, - Local error, - Local sites); - -#if defined(V8_OS_WIN) -/** - * Callback to selectively enable ETW tracing based on the document URL. - * Implemented by the embedder, it should never call back into V8. - * - * Windows allows passing additional data to the ETW EnableCallback: - * https://learn.microsoft.com/en-us/windows/win32/api/evntprov/nc-evntprov-penablecallback - * - * This data can be configured in a WPR (Windows Performance Recorder) - * profile, adding a CustomFilter to an EventProvider like the following: - * - * - * - * - * - * Where: - * - Name="57277741-3638-4A4B-BDBA-0AC6E45DA56C" is the GUID of the V8 - * ETW provider, (see src/libplatform/etw/etw-provider-win.h), - * - Type="0x80000000" is EVENT_FILTER_TYPE_SCHEMATIZED, - * - Value="AQABAAAAAA..." is a base64-encoded byte array that is - * base64-decoded by Windows and passed to the ETW enable callback in - * the 'PEVENT_FILTER_DESCRIPTOR FilterData' argument; see: - * https://learn.microsoft.com/en-us/windows/win32/api/evntprov/ns-evntprov-event_filter_descriptor. - * - * This array contains a struct EVENT_FILTER_HEADER followed by a - * variable length payload, and as payload we pass a string in JSON format, - * with a list of regular expressions that should match the document URL - * in order to enable ETW tracing: - * { - * "version": "1.0", - * "filtered_urls": [ - * "https:\/\/.*\.chromium\.org\/.*", "https://v8.dev/";, "..." - * ] - * } - */ -using FilterETWSessionByURLCallback = - bool (*)(Local context, const std::string& etw_filter_payload); -#endif // V8_OS_WIN - -} // namespace v8 - -#endif // INCLUDE_V8_ISOLATE_CALLBACKS_H_ diff --git a/NativeScript/napi/v8/include/v8-container.h b/NativeScript/napi/v8/include/v8-container.h deleted file mode 100644 index 1d9e72c1..00000000 --- a/NativeScript/napi/v8/include/v8-container.h +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_CONTAINER_H_ -#define INCLUDE_V8_CONTAINER_H_ - -#include -#include - -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-object.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Context; -class Isolate; - -/** - * An instance of the built-in array constructor (ECMA-262, 15.4.2). - */ -class V8_EXPORT Array : public Object { - public: - uint32_t Length() const; - - /** - * Creates a JavaScript array with the given length. If the length - * is negative the returned array will have length 0. - */ - static Local New(Isolate* isolate, int length = 0); - - /** - * Creates a JavaScript array out of a Local array in C++ - * with a known length. - */ - static Local New(Isolate* isolate, Local* elements, - size_t length); - V8_INLINE static Array* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - enum class CallbackResult { - kException, - kBreak, - kContinue, - }; - using IterationCallback = CallbackResult (*)(uint32_t index, - Local element, - void* data); - - /** - * Calls {callback} for every element of this array, passing {callback_data} - * as its {data} parameter. - * This function will typically be faster than calling {Get()} repeatedly. - * As a consequence of being optimized for low overhead, the provided - * callback must adhere to the following restrictions: - * - It must not allocate any V8 objects and continue iterating; it may - * allocate (e.g. an error message/object) and then immediately terminate - * the iteration. - * - It must not modify the array being iterated. - * - It must not call back into V8 (unless it can guarantee that such a - * call does not violate the above restrictions, which is difficult). - * - The {Local element} must not "escape", i.e. must not be assigned - * to any other {Local}. Creating a {Global} from it, or updating a - * v8::TypecheckWitness with it, is safe. - * These restrictions may be lifted in the future if use cases arise that - * justify a slower but more robust implementation. - * - * Returns {Nothing} on exception; use a {TryCatch} to catch and handle this - * exception. - * When the {callback} returns {kException}, iteration is terminated - * immediately, returning {Nothing}. By returning {kBreak}, the callback - * can request non-exceptional early termination of the iteration. - */ - Maybe Iterate(Local context, IterationCallback callback, - void* callback_data); - - private: - Array(); - static void CheckCast(Value* obj); -}; - -/** - * An instance of the built-in Map constructor (ECMA-262, 6th Edition, 23.1.1). - */ -class V8_EXPORT Map : public Object { - public: - size_t Size() const; - void Clear(); - V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, - Local key); - V8_WARN_UNUSED_RESULT MaybeLocal Set(Local context, - Local key, - Local value); - V8_WARN_UNUSED_RESULT Maybe Has(Local context, - Local key); - V8_WARN_UNUSED_RESULT Maybe Delete(Local context, - Local key); - - /** - * Returns an array of length Size() * 2, where index N is the Nth key and - * index N + 1 is the Nth value. - */ - Local AsArray() const; - - /** - * Creates a new empty Map. - */ - static Local New(Isolate* isolate); - - V8_INLINE static Map* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - private: - Map(); - static void CheckCast(Value* obj); -}; - -/** - * An instance of the built-in Set constructor (ECMA-262, 6th Edition, 23.2.1). - */ -class V8_EXPORT Set : public Object { - public: - size_t Size() const; - void Clear(); - V8_WARN_UNUSED_RESULT MaybeLocal Add(Local context, - Local key); - V8_WARN_UNUSED_RESULT Maybe Has(Local context, - Local key); - V8_WARN_UNUSED_RESULT Maybe Delete(Local context, - Local key); - - /** - * Returns an array of the keys in this Set. - */ - Local AsArray() const; - - /** - * Creates a new empty Set. - */ - static Local New(Isolate* isolate); - - V8_INLINE static Set* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - private: - Set(); - static void CheckCast(Value* obj); -}; - -} // namespace v8 - -#endif // INCLUDE_V8_CONTAINER_H_ diff --git a/NativeScript/napi/v8/include/v8-context.h b/NativeScript/napi/v8/include/v8-context.h deleted file mode 100644 index 50c7e6f4..00000000 --- a/NativeScript/napi/v8/include/v8-context.h +++ /dev/null @@ -1,456 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_CONTEXT_H_ -#define INCLUDE_V8_CONTEXT_H_ - -#include - -#include - -#include "v8-data.h" // NOLINT(build/include_directory) -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-maybe.h" // NOLINT(build/include_directory) -#include "v8-snapshot.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Function; -class MicrotaskQueue; -class Object; -class ObjectTemplate; -class Value; -class String; - -/** - * A container for extension names. - */ -class V8_EXPORT ExtensionConfiguration { - public: - ExtensionConfiguration() : name_count_(0), names_(nullptr) {} - ExtensionConfiguration(int name_count, const char* names[]) - : name_count_(name_count), names_(names) {} - - const char** begin() const { return &names_[0]; } - const char** end() const { return &names_[name_count_]; } - - private: - const int name_count_; - const char** names_; -}; - -/** - * A sandboxed execution context with its own set of built-in objects - * and functions. - */ -class V8_EXPORT Context : public Data { - public: - /** - * Returns the global proxy object. - * - * Global proxy object is a thin wrapper whose prototype points to actual - * context's global object with the properties like Object, etc. This is done - * that way for security reasons (for more details see - * https://wiki.mozilla.org/Gecko:SplitWindow). - * - * Please note that changes to global proxy object prototype most probably - * would break VM---v8 expects only global object as a prototype of global - * proxy object. - */ - Local Global(); - - /** - * Detaches the global object from its context before - * the global object can be reused to create a new context. - */ - void DetachGlobal(); - - /** - * Creates a new context and returns a handle to the newly allocated - * context. - * - * \param isolate The isolate in which to create the context. - * - * \param extensions An optional extension configuration containing - * the extensions to be installed in the newly created context. - * - * \param global_template An optional object template from which the - * global object for the newly created context will be created. - * - * \param global_object An optional global object to be reused for - * the newly created context. This global object must have been - * created by a previous call to Context::New with the same global - * template. The state of the global object will be completely reset - * and only object identify will remain. - */ - static Local New( - Isolate* isolate, ExtensionConfiguration* extensions = nullptr, - MaybeLocal global_template = MaybeLocal(), - MaybeLocal global_object = MaybeLocal(), - DeserializeInternalFieldsCallback internal_fields_deserializer = - DeserializeInternalFieldsCallback(), - MicrotaskQueue* microtask_queue = nullptr); - - /** - * Create a new context from a (non-default) context snapshot. There - * is no way to provide a global object template since we do not create - * a new global object from template, but we can reuse a global object. - * - * \param isolate See v8::Context::New. - * - * \param context_snapshot_index The index of the context snapshot to - * deserialize from. Use v8::Context::New for the default snapshot. - * - * \param embedder_fields_deserializer Optional callback to deserialize - * internal fields. It should match the SerializeInternalFieldCallback used - * to serialize. - * - * \param extensions See v8::Context::New. - * - * \param global_object See v8::Context::New. - */ - static MaybeLocal FromSnapshot( - Isolate* isolate, size_t context_snapshot_index, - DeserializeInternalFieldsCallback embedder_fields_deserializer = - DeserializeInternalFieldsCallback(), - ExtensionConfiguration* extensions = nullptr, - MaybeLocal global_object = MaybeLocal(), - MicrotaskQueue* microtask_queue = nullptr); - - /** - * Returns an global object that isn't backed by an actual context. - * - * The global template needs to have access checks with handlers installed. - * If an existing global object is passed in, the global object is detached - * from its context. - * - * Note that this is different from a detached context where all accesses to - * the global proxy will fail. Instead, the access check handlers are invoked. - * - * It is also not possible to detach an object returned by this method. - * Instead, the access check handlers need to return nothing to achieve the - * same effect. - * - * It is possible, however, to create a new context from the global object - * returned by this method. - */ - static MaybeLocal NewRemoteContext( - Isolate* isolate, Local global_template, - MaybeLocal global_object = MaybeLocal()); - - /** - * Sets the security token for the context. To access an object in - * another context, the security tokens must match. - */ - void SetSecurityToken(Local token); - - /** Restores the security token to the default value. */ - void UseDefaultSecurityToken(); - - /** Returns the security token of this context.*/ - Local GetSecurityToken(); - - /** - * Enter this context. After entering a context, all code compiled - * and run is compiled and run in this context. If another context - * is already entered, this old context is saved so it can be - * restored when the new context is exited. - */ - void Enter(); - - /** - * Exit this context. Exiting the current context restores the - * context that was in place when entering the current context. - */ - void Exit(); - - /** - * Delegate to help with Deep freezing embedder-specific objects (such as - * JSApiObjects) that can not be frozen natively. - */ - class DeepFreezeDelegate { - public: - /** - * Performs embedder-specific operations to freeze the provided embedder - * object. The provided object *will* be frozen by DeepFreeze after this - * function returns, so only embedder-specific objects need to be frozen. - * This function *may not* create new JS objects or perform JS allocations. - * Any v8 objects reachable from the provided embedder object that should - * also be considered for freezing should be added to the children_out - * parameter. Returns true if the operation completed successfully. - */ - virtual bool FreezeEmbedderObjectAndGetChildren( - Local obj, std::vector>& children_out) = 0; - }; - - /** - * Attempts to recursively freeze all objects reachable from this context. - * Some objects (generators, iterators, non-const closures) can not be frozen - * and will cause this method to throw an error. An optional delegate can be - * provided to help freeze embedder-specific objects. - * - * Freezing occurs in two steps: - * 1. "Marking" where we iterate through all objects reachable by this - * context, accumulating a list of objects that need to be frozen and - * looking for objects that can't be frozen. This step is separated because - * it is more efficient when we can assume there is no garbage collection. - * 2. "Freezing" where we go through the list of objects and freezing them. - * This effectively requires copying them so it may trigger garbage - * collection. - */ - Maybe DeepFreeze(DeepFreezeDelegate* delegate = nullptr); - - /** Returns the isolate associated with a current context. */ - Isolate* GetIsolate(); - - /** Returns the microtask queue associated with a current context. */ - MicrotaskQueue* GetMicrotaskQueue(); - - /** Sets the microtask queue associated with the current context. */ - void SetMicrotaskQueue(MicrotaskQueue* queue); - - /** - * The field at kDebugIdIndex used to be reserved for the inspector. - * It now serves no purpose. - */ - enum EmbedderDataFields { kDebugIdIndex = 0 }; - - /** - * Return the number of fields allocated for embedder data. - */ - uint32_t GetNumberOfEmbedderDataFields(); - - /** - * Gets the embedder data with the given index, which must have been set by a - * previous call to SetEmbedderData with the same index. - */ - V8_INLINE Local GetEmbedderData(int index); - - /** - * Gets the binding object used by V8 extras. Extra natives get a reference - * to this object and can use it to "export" functionality by adding - * properties. Extra natives can also "import" functionality by accessing - * properties added by the embedder using the V8 API. - */ - Local GetExtrasBindingObject(); - - /** - * Sets the embedder data with the given index, growing the data as - * needed. Note that index 0 currently has a special meaning for Chrome's - * debugger. - */ - void SetEmbedderData(int index, Local value); - - /** - * Gets a 2-byte-aligned native pointer from the embedder data with the given - * index, which must have been set by a previous call to - * SetAlignedPointerInEmbedderData with the same index. Note that index 0 - * currently has a special meaning for Chrome's debugger. - */ - V8_INLINE void* GetAlignedPointerFromEmbedderData(int index); - - /** - * Sets a 2-byte-aligned native pointer in the embedder data with the given - * index, growing the data as needed. Note that index 0 currently has a - * special meaning for Chrome's debugger. - */ - void SetAlignedPointerInEmbedderData(int index, void* value); - - /** - * Control whether code generation from strings is allowed. Calling - * this method with false will disable 'eval' and the 'Function' - * constructor for code running in this context. If 'eval' or the - * 'Function' constructor are used an exception will be thrown. - * - * If code generation from strings is not allowed the - * V8::AllowCodeGenerationFromStrings callback will be invoked if - * set before blocking the call to 'eval' or the 'Function' - * constructor. If that callback returns true, the call will be - * allowed, otherwise an exception will be thrown. If no callback is - * set an exception will be thrown. - */ - void AllowCodeGenerationFromStrings(bool allow); - - /** - * Returns true if code generation from strings is allowed for the context. - * For more details see AllowCodeGenerationFromStrings(bool) documentation. - */ - bool IsCodeGenerationFromStringsAllowed() const; - - /** - * Sets the error description for the exception that is thrown when - * code generation from strings is not allowed and 'eval' or the 'Function' - * constructor are called. - */ - void SetErrorMessageForCodeGenerationFromStrings(Local message); - - /** - * Sets the error description for the exception that is thrown when - * wasm code generation is not allowed. - */ - void SetErrorMessageForWasmCodeGeneration(Local message); - - /** - * Return data that was previously attached to the context snapshot via - * SnapshotCreator, and removes the reference to it. - * Repeated call with the same index returns an empty MaybeLocal. - */ - template - V8_INLINE MaybeLocal GetDataFromSnapshotOnce(size_t index); - - /** - * If callback is set, abort any attempt to execute JavaScript in this - * context, call the specified callback, and throw an exception. - * To unset abort, pass nullptr as callback. - */ - using AbortScriptExecutionCallback = void (*)(Isolate* isolate, - Local context); - void SetAbortScriptExecution(AbortScriptExecutionCallback callback); - - /** - * Returns the value that was set or restored by - * SetContinuationPreservedEmbedderData(), if any. - */ - Local GetContinuationPreservedEmbedderData() const; - - /** - * Sets a value that will be stored on continuations and reset while the - * continuation runs. - */ - void SetContinuationPreservedEmbedderData(Local context); - - /** - * Set or clear hooks to be invoked for promise lifecycle operations. - * To clear a hook, set it to an empty v8::Function. Each function will - * receive the observed promise as the first argument. If a chaining - * operation is used on a promise, the init will additionally receive - * the parent promise as the second argument. - */ - void SetPromiseHooks(Local init_hook, Local before_hook, - Local after_hook, - Local resolve_hook); - - bool HasTemplateLiteralObject(Local object); - /** - * Stack-allocated class which sets the execution context for all - * operations executed within a local scope. - */ - class V8_NODISCARD Scope { - public: - explicit V8_INLINE Scope(Local context) : context_(context) { - context_->Enter(); - } - V8_INLINE ~Scope() { context_->Exit(); } - - private: - Local context_; - }; - - /** - * Stack-allocated class to support the backup incumbent settings object - * stack. - * https://html.spec.whatwg.org/multipage/webappapis.html#backup-incumbent-settings-object-stack - */ - class V8_EXPORT V8_NODISCARD BackupIncumbentScope final { - public: - /** - * |backup_incumbent_context| is pushed onto the backup incumbent settings - * object stack. - */ - explicit BackupIncumbentScope(Local backup_incumbent_context); - ~BackupIncumbentScope(); - - private: - friend class internal::Isolate; - - uintptr_t JSStackComparableAddressPrivate() const { - return js_stack_comparable_address_; - } - - Local backup_incumbent_context_; - uintptr_t js_stack_comparable_address_ = 0; - const BackupIncumbentScope* prev_ = nullptr; - }; - - V8_INLINE static Context* Cast(Data* data); - - private: - friend class Value; - friend class Script; - friend class Object; - friend class Function; - - static void CheckCast(Data* obj); - - internal::Address* GetDataFromSnapshotOnce(size_t index); - Local SlowGetEmbedderData(int index); - void* SlowGetAlignedPointerFromEmbedderData(int index); -}; - -// --- Implementation --- - -Local Context::GetEmbedderData(int index) { -#ifndef V8_ENABLE_CHECKS - using A = internal::Address; - using I = internal::Internals; - A ctx = internal::ValueHelper::ValueAsAddress(this); - A embedder_data = - I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); - int value_offset = - I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); - A value = I::ReadRawField(embedder_data, value_offset); -#ifdef V8_COMPRESS_POINTERS - // We read the full pointer value and then decompress it in order to avoid - // dealing with potential endiannes issues. - value = I::DecompressTaggedField(embedder_data, static_cast(value)); -#endif - - auto isolate = reinterpret_cast( - internal::IsolateFromNeverReadOnlySpaceObject(ctx)); - return Local::New(isolate, value); -#else - return SlowGetEmbedderData(index); -#endif -} - -void* Context::GetAlignedPointerFromEmbedderData(int index) { -#if !defined(V8_ENABLE_CHECKS) - using A = internal::Address; - using I = internal::Internals; - A ctx = internal::ValueHelper::ValueAsAddress(this); - A embedder_data = - I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); - int value_offset = I::kEmbedderDataArrayHeaderSize + - (I::kEmbedderDataSlotSize * index) + - I::kEmbedderDataSlotExternalPointerOffset; - Isolate* isolate = I::GetIsolateForSandbox(ctx); - return reinterpret_cast( - I::ReadExternalPointerField( - isolate, embedder_data, value_offset)); -#else - return SlowGetAlignedPointerFromEmbedderData(index); -#endif -} - -template -MaybeLocal Context::GetDataFromSnapshotOnce(size_t index) { - auto slot = GetDataFromSnapshotOnce(index); - if (slot) { - internal::PerformCastCheck( - internal::ValueHelper::SlotAsValue(slot)); - } - return Local::FromSlot(slot); -} - -Context* Context::Cast(v8::Data* data) { -#ifdef V8_ENABLE_CHECKS - CheckCast(data); -#endif - return static_cast(data); -} - -} // namespace v8 - -#endif // INCLUDE_V8_CONTEXT_H_ diff --git a/NativeScript/napi/v8/include/v8-cppgc.h b/NativeScript/napi/v8/include/v8-cppgc.h deleted file mode 100644 index e0d76f45..00000000 --- a/NativeScript/napi/v8/include/v8-cppgc.h +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_CPPGC_H_ -#define INCLUDE_V8_CPPGC_H_ - -#include -#include -#include - -#include "cppgc/common.h" -#include "cppgc/custom-space.h" -#include "cppgc/heap-statistics.h" -#include "cppgc/visitor.h" -#include "v8-internal.h" // NOLINT(build/include_directory) -#include "v8-platform.h" // NOLINT(build/include_directory) -#include "v8-traced-handle.h" // NOLINT(build/include_directory) - -namespace cppgc { -class AllocationHandle; -class HeapHandle; -} // namespace cppgc - -namespace v8 { - -class Object; - -namespace internal { -class CppHeap; -} // namespace internal - -class CustomSpaceStatisticsReceiver; - -/** - * Describes how V8 wrapper objects maintain references to garbage-collected C++ - * objects. - */ -struct WrapperDescriptor final { - /** - * The index used on `v8::Ojbect::SetAlignedPointerFromInternalField()` and - * related APIs to add additional data to an object which is used to identify - * JS->C++ references. - */ - using InternalFieldIndex = int; - - /** - * Unknown embedder id. The value is reserved for internal usages and must not - * be used with `CppHeap`. - */ - static constexpr uint16_t kUnknownEmbedderId = UINT16_MAX; - - constexpr WrapperDescriptor(InternalFieldIndex wrappable_type_index, - InternalFieldIndex wrappable_instance_index, - uint16_t embedder_id_for_garbage_collected) - : wrappable_type_index(wrappable_type_index), - wrappable_instance_index(wrappable_instance_index), - embedder_id_for_garbage_collected(embedder_id_for_garbage_collected) {} - - /** - * Index of the wrappable type. - */ - InternalFieldIndex wrappable_type_index; - - /** - * Index of the wrappable instance. - */ - InternalFieldIndex wrappable_instance_index; - - /** - * Embedder id identifying instances of garbage-collected objects. It is - * expected that the first field of the wrappable type is a uint16_t holding - * the id. Only references to instances of wrappables types with an id of - * `embedder_id_for_garbage_collected` will be considered by CppHeap. - */ - uint16_t embedder_id_for_garbage_collected; -}; - -struct V8_EXPORT CppHeapCreateParams { - CppHeapCreateParams( - std::vector> custom_spaces, - WrapperDescriptor wrapper_descriptor) - : custom_spaces(std::move(custom_spaces)), - wrapper_descriptor(wrapper_descriptor) {} - - CppHeapCreateParams(const CppHeapCreateParams&) = delete; - CppHeapCreateParams& operator=(const CppHeapCreateParams&) = delete; - - std::vector> custom_spaces; - WrapperDescriptor wrapper_descriptor; - /** - * Specifies which kind of marking are supported by the heap. The type may be - * further reduced via runtime flags when attaching the heap to an Isolate. - */ - cppgc::Heap::MarkingType marking_support = - cppgc::Heap::MarkingType::kIncrementalAndConcurrent; - /** - * Specifies which kind of sweeping is supported by the heap. The type may be - * further reduced via runtime flags when attaching the heap to an Isolate. - */ - cppgc::Heap::SweepingType sweeping_support = - cppgc::Heap::SweepingType::kIncrementalAndConcurrent; -}; - -/** - * A heap for allocating managed C++ objects. - * - * Similar to v8::Isolate, the heap may only be accessed from one thread at a - * time. The heap may be used from different threads using the - * v8::Locker/v8::Unlocker APIs which is different from generic Oilpan. - */ -class V8_EXPORT CppHeap { - public: - static std::unique_ptr Create(v8::Platform* platform, - const CppHeapCreateParams& params); - - virtual ~CppHeap() = default; - - /** - * \returns the opaque handle for allocating objects using - * `MakeGarbageCollected()`. - */ - cppgc::AllocationHandle& GetAllocationHandle(); - - /** - * \returns the opaque heap handle which may be used to refer to this heap in - * other APIs. Valid as long as the underlying `CppHeap` is alive. - */ - cppgc::HeapHandle& GetHeapHandle(); - - /** - * Terminate clears all roots and performs multiple garbage collections to - * reclaim potentially newly created objects in destructors. - * - * After this call, object allocation is prohibited. - */ - void Terminate(); - - /** - * \param detail_level specifies whether should return detailed - * statistics or only brief summary statistics. - * \returns current CppHeap statistics regarding memory consumption - * and utilization. - */ - cppgc::HeapStatistics CollectStatistics( - cppgc::HeapStatistics::DetailLevel detail_level); - - /** - * Collects statistics for the given spaces and reports them to the receiver. - * - * \param custom_spaces a collection of custom space indicies. - * \param receiver an object that gets the results. - */ - void CollectCustomSpaceStatisticsAtLastGC( - std::vector custom_spaces, - std::unique_ptr receiver); - - /** - * Enables a detached mode that allows testing garbage collection using - * `cppgc::testing` APIs. Once used, the heap cannot be attached to an - * `Isolate` anymore. - */ - void EnableDetachedGarbageCollectionsForTesting(); - - /** - * Performs a stop-the-world garbage collection for testing purposes. - * - * \param stack_state The stack state to assume for the garbage collection. - */ - void CollectGarbageForTesting(cppgc::EmbedderStackState stack_state); - - /** - * Performs a stop-the-world minor garbage collection for testing purposes. - * - * \param stack_state The stack state to assume for the garbage collection. - */ - void CollectGarbageInYoungGenerationForTesting( - cppgc::EmbedderStackState stack_state); - - /** - * \returns the wrapper descriptor of this CppHeap. - */ - v8::WrapperDescriptor wrapper_descriptor() const; - - private: - CppHeap() = default; - - friend class internal::CppHeap; -}; - -class JSVisitor : public cppgc::Visitor { - public: - explicit JSVisitor(cppgc::Visitor::Key key) : cppgc::Visitor(key) {} - ~JSVisitor() override = default; - - void Trace(const TracedReferenceBase& ref) { - if (ref.IsEmptyThreadSafe()) return; - Visit(ref); - } - - protected: - using cppgc::Visitor::Visit; - - virtual void Visit(const TracedReferenceBase& ref) {} -}; - -/** - * Provided as input to `CppHeap::CollectCustomSpaceStatisticsAtLastGC()`. - * - * Its method is invoked with the results of the statistic collection. - */ -class CustomSpaceStatisticsReceiver { - public: - virtual ~CustomSpaceStatisticsReceiver() = default; - /** - * Reports the size of a space at the last GC. It is called for each space - * that was requested in `CollectCustomSpaceStatisticsAtLastGC()`. - * - * \param space_index The index of the space. - * \param bytes The total size of live objects in the space at the last GC. - * It is zero if there was no GC yet. - */ - virtual void AllocatedBytes(cppgc::CustomSpaceIndex space_index, - size_t bytes) = 0; -}; - -} // namespace v8 - -namespace cppgc { - -template -struct TraceTrait> { - static cppgc::TraceDescriptor GetTraceDescriptor(const void* self) { - return {nullptr, Trace}; - } - - static void Trace(Visitor* visitor, const void* self) { - static_cast(visitor)->Trace( - *static_cast*>(self)); - } -}; - -} // namespace cppgc - -#endif // INCLUDE_V8_CPPGC_H_ diff --git a/NativeScript/napi/v8/include/v8-data.h b/NativeScript/napi/v8/include/v8-data.h deleted file mode 100644 index fc4dea92..00000000 --- a/NativeScript/napi/v8/include/v8-data.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_DATA_H_ -#define INCLUDE_V8_DATA_H_ - -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Context; - -/** - * The superclass of objects that can reside on V8's heap. - */ -class V8_EXPORT Data { - public: - /** - * Returns true if this data is a |v8::Value|. - */ - bool IsValue() const; - - /** - * Returns true if this data is a |v8::Module|. - */ - bool IsModule() const; - - /** - * Returns tru if this data is a |v8::FixedArray| - */ - bool IsFixedArray() const; - - /** - * Returns true if this data is a |v8::Private|. - */ - bool IsPrivate() const; - - /** - * Returns true if this data is a |v8::ObjectTemplate|. - */ - bool IsObjectTemplate() const; - - /** - * Returns true if this data is a |v8::FunctionTemplate|. - */ - bool IsFunctionTemplate() const; - - /** - * Returns true if this data is a |v8::Context|. - */ - bool IsContext() const; - - private: - Data() = delete; -}; - -/** - * A fixed-sized array with elements of type Data. - */ -class V8_EXPORT FixedArray : public Data { - public: - int Length() const; - Local Get(Local context, int i) const; - - V8_INLINE static FixedArray* Cast(Data* data) { -#ifdef V8_ENABLE_CHECKS - CheckCast(data); -#endif - return reinterpret_cast(data); - } - - private: - static void CheckCast(Data* obj); -}; - -} // namespace v8 - -#endif // INCLUDE_V8_DATA_H_ diff --git a/NativeScript/napi/v8/include/v8-date.h b/NativeScript/napi/v8/include/v8-date.h deleted file mode 100644 index 8d82ccc9..00000000 --- a/NativeScript/napi/v8/include/v8-date.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_DATE_H_ -#define INCLUDE_V8_DATE_H_ - -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-object.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Context; - -/** - * An instance of the built-in Date constructor (ECMA-262, 15.9). - */ -class V8_EXPORT Date : public Object { - public: - static V8_WARN_UNUSED_RESULT MaybeLocal New(Local context, - double time); - - /** - * A specialization of Value::NumberValue that is more efficient - * because we know the structure of this object. - */ - double ValueOf() const; - - /** - * Generates ISO string representation. - */ - v8::Local ToISOString() const; - - V8_INLINE static Date* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - private: - static void CheckCast(Value* obj); -}; - -} // namespace v8 - -#endif // INCLUDE_V8_DATE_H_ diff --git a/NativeScript/napi/v8/include/v8-debug.h b/NativeScript/napi/v8/include/v8-debug.h deleted file mode 100644 index 52255f37..00000000 --- a/NativeScript/napi/v8/include/v8-debug.h +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_DEBUG_H_ -#define INCLUDE_V8_DEBUG_H_ - -#include - -#include "v8-script.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Isolate; -class String; - -/** - * A single JavaScript stack frame. - */ -class V8_EXPORT StackFrame { - public: - /** - * Returns the source location, 0-based, for the associated function call. - */ - Location GetLocation() const; - - /** - * Returns the number, 1-based, of the line for the associate function call. - * This method will return Message::kNoLineNumberInfo if it is unable to - * retrieve the line number, or if kLineNumber was not passed as an option - * when capturing the StackTrace. - */ - int GetLineNumber() const { return GetLocation().GetLineNumber() + 1; } - - /** - * Returns the 1-based column offset on the line for the associated function - * call. - * This method will return Message::kNoColumnInfo if it is unable to retrieve - * the column number, or if kColumnOffset was not passed as an option when - * capturing the StackTrace. - */ - int GetColumn() const { return GetLocation().GetColumnNumber() + 1; } - - /** - * Returns the id of the script for the function for this StackFrame. - * This method will return Message::kNoScriptIdInfo if it is unable to - * retrieve the script id, or if kScriptId was not passed as an option when - * capturing the StackTrace. - */ - int GetScriptId() const; - - /** - * Returns the name of the resource that contains the script for the - * function for this StackFrame. - */ - Local GetScriptName() const; - - /** - * Returns the name of the resource that contains the script for the - * function for this StackFrame or sourceURL value if the script name - * is undefined and its source ends with //# sourceURL=... string or - * deprecated //@ sourceURL=... string. - */ - Local GetScriptNameOrSourceURL() const; - - /** - * Returns the source of the script for the function for this StackFrame. - */ - Local GetScriptSource() const; - - /** - * Returns the source mapping URL (if one is present) of the script for - * the function for this StackFrame. - */ - Local GetScriptSourceMappingURL() const; - - /** - * Returns the name of the function associated with this stack frame. - */ - Local GetFunctionName() const; - - /** - * Returns whether or not the associated function is compiled via a call to - * eval(). - */ - bool IsEval() const; - - /** - * Returns whether or not the associated function is called as a - * constructor via "new". - */ - bool IsConstructor() const; - - /** - * Returns whether or not the associated functions is defined in wasm. - */ - bool IsWasm() const; - - /** - * Returns whether or not the associated function is defined by the user. - */ - bool IsUserJavaScript() const; -}; - -/** - * Representation of a JavaScript stack trace. The information collected is a - * snapshot of the execution stack and the information remains valid after - * execution continues. - */ -class V8_EXPORT StackTrace { - public: - /** - * Flags that determine what information is placed captured for each - * StackFrame when grabbing the current stack trace. - * Note: these options are deprecated and we always collect all available - * information (kDetailed). - */ - enum StackTraceOptions { - kLineNumber = 1, - kColumnOffset = 1 << 1 | kLineNumber, - kScriptName = 1 << 2, - kFunctionName = 1 << 3, - kIsEval = 1 << 4, - kIsConstructor = 1 << 5, - kScriptNameOrSourceURL = 1 << 6, - kScriptId = 1 << 7, - kExposeFramesAcrossSecurityOrigins = 1 << 8, - kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName, - kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL - }; - - /** - * Returns a StackFrame at a particular index. - */ - Local GetFrame(Isolate* isolate, uint32_t index) const; - - /** - * Returns the number of StackFrames. - */ - int GetFrameCount() const; - - /** - * Grab a snapshot of the current JavaScript execution stack. - * - * \param frame_limit The maximum number of stack frames we want to capture. - * \param options Enumerates the set of things we will capture for each - * StackFrame. - */ - static Local CurrentStackTrace( - Isolate* isolate, int frame_limit, StackTraceOptions options = kDetailed); - - /** - * Returns the first valid script name or source URL starting at the top of - * the JS stack. The returned string is either an empty handle if no script - * name/url was found or a non-zero-length string. - * - * This method is equivalent to calling StackTrace::CurrentStackTrace and - * walking the resulting frames from the beginning until a non-empty script - * name/url is found. The difference is that this method won't allocate - * a stack trace. - */ - static Local CurrentScriptNameOrSourceURL(Isolate* isolate); -}; - -} // namespace v8 - -#endif // INCLUDE_V8_DEBUG_H_ diff --git a/NativeScript/napi/v8/include/v8-embedder-heap.h b/NativeScript/napi/v8/include/v8-embedder-heap.h deleted file mode 100644 index c37dadf7..00000000 --- a/NativeScript/napi/v8/include/v8-embedder-heap.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_EMBEDDER_HEAP_H_ -#define INCLUDE_V8_EMBEDDER_HEAP_H_ - -#include "v8-traced-handle.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Isolate; -class Value; - -/** - * Handler for embedder roots on non-unified heap garbage collections. - */ -class V8_EXPORT EmbedderRootsHandler { - public: - virtual ~EmbedderRootsHandler() = default; - - /** - * Returns true if the |TracedReference| handle should be considered as root - * for the currently running non-tracing garbage collection and false - * otherwise. The default implementation will keep all |TracedReference| - * references as roots. - * - * If this returns false, then V8 may decide that the object referred to by - * such a handle is reclaimed. In that case, V8 calls |ResetRoot()| for the - * |TracedReference|. - * - * Note that the `handle` is different from the handle that the embedder holds - * for retaining the object. The embedder may use |WrapperClassId()| to - * distinguish cases where it wants handles to be treated as roots from not - * being treated as roots. - * - * The concrete implementations must be thread-safe. - */ - virtual bool IsRoot(const v8::TracedReference& handle) = 0; - - /** - * Used in combination with |IsRoot|. Called by V8 when an - * object that is backed by a handle is reclaimed by a non-tracing garbage - * collection. It is up to the embedder to reset the original handle. - * - * Note that the |handle| is different from the handle that the embedder holds - * for retaining the object. It is up to the embedder to find the original - * handle via the object or class id. - */ - virtual void ResetRoot(const v8::TracedReference& handle) = 0; - - /** - * Similar to |ResetRoot()|, but opportunistic. The function is called in - * parallel for different handles and as such must be thread-safe. In case, - * |false| is returned, |ResetRoot()| will be recalled for the same handle. - */ - virtual bool TryResetRoot(const v8::TracedReference& handle) { - ResetRoot(handle); - return true; - } -}; - -} // namespace v8 - -#endif // INCLUDE_V8_EMBEDDER_HEAP_H_ diff --git a/NativeScript/napi/v8/include/v8-embedder-state-scope.h b/NativeScript/napi/v8/include/v8-embedder-state-scope.h deleted file mode 100644 index d8a3b08d..00000000 --- a/NativeScript/napi/v8/include/v8-embedder-state-scope.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_ -#define INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_ - -#include - -#include "v8-context.h" // NOLINT(build/include_directory) -#include "v8-internal.h" // NOLINT(build/include_directory) -#include "v8-local-handle.h" // NOLINT(build/include_directory) - -namespace v8 { - -namespace internal { -class EmbedderState; -} // namespace internal - -// A StateTag represents a possible state of the embedder. -enum class EmbedderStateTag : uint8_t { - // reserved - EMPTY = 0, - OTHER = 1, - // embedder can define any state after -}; - -// A stack-allocated class that manages an embedder state on the isolate. -// After an EmbedderState scope has been created, a new embedder state will be -// pushed on the isolate stack. -class V8_EXPORT EmbedderStateScope { - public: - EmbedderStateScope(Isolate* isolate, Local context, - EmbedderStateTag tag); - - ~EmbedderStateScope(); - - private: - // Declaring operator new and delete as deleted is not spec compliant. - // Therefore declare them private instead to disable dynamic alloc - void* operator new(size_t size); - void* operator new[](size_t size); - void operator delete(void*, size_t); - void operator delete[](void*, size_t); - - std::unique_ptr embedder_state_; -}; - -} // namespace v8 - -#endif // INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_ diff --git a/NativeScript/napi/v8/include/v8-exception.h b/NativeScript/napi/v8/include/v8-exception.h deleted file mode 100644 index 3b76636c..00000000 --- a/NativeScript/napi/v8/include/v8-exception.h +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_EXCEPTION_H_ -#define INCLUDE_V8_EXCEPTION_H_ - -#include - -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Context; -class Isolate; -class Message; -class StackTrace; -class String; -class Value; - -namespace internal { -class Isolate; -class ThreadLocalTop; -} // namespace internal - -/** - * Create new error objects by calling the corresponding error object - * constructor with the message. - */ -class V8_EXPORT Exception { - public: - static Local RangeError(Local message, - Local options = {}); - static Local ReferenceError(Local message, - Local options = {}); - static Local SyntaxError(Local message, - Local options = {}); - static Local TypeError(Local message, - Local options = {}); - static Local WasmCompileError(Local message, - Local options = {}); - static Local WasmLinkError(Local message, - Local options = {}); - static Local WasmRuntimeError(Local message, - Local options = {}); - static Local Error(Local message, Local options = {}); - - /** - * Creates an error message for the given exception. - * Will try to reconstruct the original stack trace from the exception value, - * or capture the current stack trace if not available. - */ - static Local CreateMessage(Isolate* isolate, Local exception); - - /** - * Returns the original stack trace that was captured at the creation time - * of a given exception, or an empty handle if not available. - */ - static Local GetStackTrace(Local exception); -}; - -/** - * An external exception handler. - */ -class V8_EXPORT TryCatch { - public: - /** - * Creates a new try/catch block and registers it with v8. Note that - * all TryCatch blocks should be stack allocated because the memory - * location itself is compared against JavaScript try/catch blocks. - */ - explicit TryCatch(Isolate* isolate); - - /** - * Unregisters and deletes this try/catch block. - */ - ~TryCatch(); - - /** - * Returns true if an exception has been caught by this try/catch block. - */ - bool HasCaught() const; - - /** - * For certain types of exceptions, it makes no sense to continue execution. - * - * If CanContinue returns false, the correct action is to perform any C++ - * cleanup needed and then return. If CanContinue returns false and - * HasTerminated returns true, it is possible to call - * CancelTerminateExecution in order to continue calling into the engine. - */ - bool CanContinue() const; - - /** - * Returns true if an exception has been caught due to script execution - * being terminated. - * - * There is no JavaScript representation of an execution termination - * exception. Such exceptions are thrown when the TerminateExecution - * methods are called to terminate a long-running script. - * - * If such an exception has been thrown, HasTerminated will return true, - * indicating that it is possible to call CancelTerminateExecution in order - * to continue calling into the engine. - */ - bool HasTerminated() const; - - /** - * Throws the exception caught by this TryCatch in a way that avoids - * it being caught again by this same TryCatch. As with ThrowException - * it is illegal to execute any JavaScript operations after calling - * ReThrow; the caller must return immediately to where the exception - * is caught. - */ - Local ReThrow(); - - /** - * Returns the exception caught by this try/catch block. If no exception has - * been caught an empty handle is returned. - */ - Local Exception() const; - - /** - * Returns the .stack property of an object. If no .stack - * property is present an empty handle is returned. - */ - V8_WARN_UNUSED_RESULT static MaybeLocal StackTrace( - Local context, Local exception); - - /** - * Returns the .stack property of the thrown object. If no .stack property is - * present or if this try/catch block has not caught an exception, an empty - * handle is returned. - */ - V8_WARN_UNUSED_RESULT MaybeLocal StackTrace( - Local context) const; - - /** - * Returns the message associated with this exception. If there is - * no message associated an empty handle is returned. - */ - Local Message() const; - - /** - * Clears any exceptions that may have been caught by this try/catch block. - * After this method has been called, HasCaught() will return false. Cancels - * the scheduled exception if it is caught and ReThrow() is not called before. - * - * It is not necessary to clear a try/catch block before using it again; if - * another exception is thrown the previously caught exception will just be - * overwritten. However, it is often a good idea since it makes it easier - * to determine which operation threw a given exception. - */ - void Reset(); - - /** - * Set verbosity of the external exception handler. - * - * By default, exceptions that are caught by an external exception - * handler are not reported. Call SetVerbose with true on an - * external exception handler to have exceptions caught by the - * handler reported as if they were not caught. - */ - void SetVerbose(bool value); - - /** - * Returns true if verbosity is enabled. - */ - bool IsVerbose() const; - - /** - * Set whether or not this TryCatch should capture a Message object - * which holds source information about where the exception - * occurred. True by default. - */ - void SetCaptureMessage(bool value); - - TryCatch(const TryCatch&) = delete; - void operator=(const TryCatch&) = delete; - - private: - // Declaring operator new and delete as deleted is not spec compliant. - // Therefore declare them private instead to disable dynamic alloc - void* operator new(size_t size); - void* operator new[](size_t size); - void operator delete(void*, size_t); - void operator delete[](void*, size_t); - - /** - * There are cases when the raw address of C++ TryCatch object cannot be - * used for comparisons with addresses into the JS stack. The cases are: - * 1) ARM, ARM64 and MIPS simulators which have separate JS stack. - * 2) Address sanitizer allocates local C++ object in the heap when - * UseAfterReturn mode is enabled. - * This method returns address that can be used for comparisons with - * addresses into the JS stack. When neither simulator nor ASAN's - * UseAfterReturn is enabled, then the address returned will be the address - * of the C++ try catch handler itself. - */ - internal::Address JSStackComparableAddressPrivate() { - return js_stack_comparable_address_; - } - - void ResetInternal(); - - internal::Isolate* i_isolate_; - TryCatch* next_; - void* exception_; - void* message_obj_; - internal::Address js_stack_comparable_address_; - bool is_verbose_ : 1; - bool can_continue_ : 1; - bool capture_message_ : 1; - bool rethrow_ : 1; - bool has_terminated_ : 1; - - friend class internal::Isolate; - friend class internal::ThreadLocalTop; -}; - -} // namespace v8 - -#endif // INCLUDE_V8_EXCEPTION_H_ diff --git a/NativeScript/napi/v8/include/v8-extension.h b/NativeScript/napi/v8/include/v8-extension.h deleted file mode 100644 index 0705e2af..00000000 --- a/NativeScript/napi/v8/include/v8-extension.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_EXTENSION_H_ -#define INCLUDE_V8_EXTENSION_H_ - -#include - -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-primitive.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class FunctionTemplate; - -// --- Extensions --- - -/** - * Ignore - */ -class V8_EXPORT Extension { - public: - // Note that the strings passed into this constructor must live as long - // as the Extension itself. - Extension(const char* name, const char* source = nullptr, int dep_count = 0, - const char** deps = nullptr, int source_length = -1); - virtual ~Extension() { delete source_; } - virtual Local GetNativeFunctionTemplate( - Isolate* isolate, Local name) { - return Local(); - } - - const char* name() const { return name_; } - size_t source_length() const { return source_length_; } - const String::ExternalOneByteStringResource* source() const { - return source_; - } - int dependency_count() const { return dep_count_; } - const char** dependencies() const { return deps_; } - void set_auto_enable(bool value) { auto_enable_ = value; } - bool auto_enable() { return auto_enable_; } - - // Disallow copying and assigning. - Extension(const Extension&) = delete; - void operator=(const Extension&) = delete; - - private: - const char* name_; - size_t source_length_; // expected to initialize before source_ - String::ExternalOneByteStringResource* source_; - int dep_count_; - const char** deps_; - bool auto_enable_; -}; - -void V8_EXPORT RegisterExtension(std::unique_ptr); - -} // namespace v8 - -#endif // INCLUDE_V8_EXTENSION_H_ diff --git a/NativeScript/napi/v8/include/v8-external.h b/NativeScript/napi/v8/include/v8-external.h deleted file mode 100644 index 2e245036..00000000 --- a/NativeScript/napi/v8/include/v8-external.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_EXTERNAL_H_ -#define INCLUDE_V8_EXTERNAL_H_ - -#include "v8-value.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Isolate; - -/** - * A JavaScript value that wraps a C++ void*. This type of value is mainly used - * to associate C++ data structures with JavaScript objects. - */ -class V8_EXPORT External : public Value { - public: - static Local New(Isolate* isolate, void* value); - V8_INLINE static External* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - void* Value() const; - - private: - static void CheckCast(v8::Value* obj); -}; - -} // namespace v8 - -#endif // INCLUDE_V8_EXTERNAL_H_ diff --git a/NativeScript/napi/v8/include/v8-fast-api-calls.h b/NativeScript/napi/v8/include/v8-fast-api-calls.h deleted file mode 100644 index e40f1068..00000000 --- a/NativeScript/napi/v8/include/v8-fast-api-calls.h +++ /dev/null @@ -1,975 +0,0 @@ -// Copyright 2020 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * This file provides additional API on top of the default one for making - * API calls, which come from embedder C++ functions. The functions are being - * called directly from optimized code, doing all the necessary typechecks - * in the compiler itself, instead of on the embedder side. Hence the "fast" - * in the name. Example usage might look like: - * - * \code - * void FastMethod(int param, bool another_param); - * - * v8::FunctionTemplate::New(isolate, SlowCallback, data, - * signature, length, constructor_behavior - * side_effect_type, - * &v8::CFunction::Make(FastMethod)); - * \endcode - * - * By design, fast calls are limited by the following requirements, which - * the embedder should enforce themselves: - * - they should not allocate on the JS heap; - * - they should not trigger JS execution. - * To enforce them, the embedder could use the existing - * v8::Isolate::DisallowJavascriptExecutionScope and a utility similar to - * Blink's NoAllocationScope: - * https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/heap/thread_state_scopes.h;l=16 - * - * Due to these limitations, it's not directly possible to report errors by - * throwing a JS exception or to otherwise do an allocation. There is an - * alternative way of creating fast calls that supports falling back to the - * slow call and then performing the necessary allocation. When one creates - * the fast method by using CFunction::MakeWithFallbackSupport instead of - * CFunction::Make, the fast callback gets as last parameter an output variable, - * through which it can request falling back to the slow call. So one might - * declare their method like: - * - * \code - * void FastMethodWithFallback(int param, FastApiCallbackOptions& options); - * \endcode - * - * If the callback wants to signal an error condition or to perform an - * allocation, it must set options.fallback to true and do an early return from - * the fast method. Then V8 checks the value of options.fallback and if it's - * true, falls back to executing the SlowCallback, which is capable of reporting - * the error (either by throwing a JS exception or logging to the console) or - * doing the allocation. It's the embedder's responsibility to ensure that the - * fast callback is idempotent up to the point where error and fallback - * conditions are checked, because otherwise executing the slow callback might - * produce visible side-effects twice. - * - * An example for custom embedder type support might employ a way to wrap/ - * unwrap various C++ types in JSObject instances, e.g: - * - * \code - * - * // Helper method with a check for field count. - * template - * inline T* GetInternalField(v8::Local wrapper) { - * assert(offset < wrapper->InternalFieldCount()); - * return reinterpret_cast( - * wrapper->GetAlignedPointerFromInternalField(offset)); - * } - * - * class CustomEmbedderType { - * public: - * // Returns the raw C object from a wrapper JS object. - * static CustomEmbedderType* Unwrap(v8::Local wrapper) { - * return GetInternalField(wrapper); - * } - * static void FastMethod(v8::Local receiver_obj, int param) { - * CustomEmbedderType* receiver = static_cast( - * receiver_obj->GetAlignedPointerFromInternalField( - * kV8EmbedderWrapperObjectIndex)); - * - * // Type checks are already done by the optimized code. - * // Then call some performance-critical method like: - * // receiver->Method(param); - * } - * - * static void SlowMethod( - * const v8::FunctionCallbackInfo& info) { - * v8::Local instance = - * v8::Local::Cast(info.Holder()); - * CustomEmbedderType* receiver = Unwrap(instance); - * // TODO: Do type checks and extract {param}. - * receiver->Method(param); - * } - * }; - * - * // TODO(mslekova): Clean-up these constants - * // The constants kV8EmbedderWrapperTypeIndex and - * // kV8EmbedderWrapperObjectIndex describe the offsets for the type info - * // struct and the native object, when expressed as internal field indices - * // within a JSObject. The existance of this helper function assumes that - * // all embedder objects have their JSObject-side type info at the same - * // offset, but this is not a limitation of the API itself. For a detailed - * // use case, see the third example. - * static constexpr int kV8EmbedderWrapperTypeIndex = 0; - * static constexpr int kV8EmbedderWrapperObjectIndex = 1; - * - * // The following setup function can be templatized based on - * // the {embedder_object} argument. - * void SetupCustomEmbedderObject(v8::Isolate* isolate, - * v8::Local context, - * CustomEmbedderType* embedder_object) { - * isolate->set_embedder_wrapper_type_index( - * kV8EmbedderWrapperTypeIndex); - * isolate->set_embedder_wrapper_object_index( - * kV8EmbedderWrapperObjectIndex); - * - * v8::CFunction c_func = - * MakeV8CFunction(CustomEmbedderType::FastMethod); - * - * Local method_template = - * v8::FunctionTemplate::New( - * isolate, CustomEmbedderType::SlowMethod, v8::Local(), - * v8::Local(), 1, v8::ConstructorBehavior::kAllow, - * v8::SideEffectType::kHasSideEffect, &c_func); - * - * v8::Local object_template = - * v8::ObjectTemplate::New(isolate); - * object_template->SetInternalFieldCount( - * kV8EmbedderWrapperObjectIndex + 1); - * object_template->Set(isolate, "method", method_template); - * - * // Instantiate the wrapper JS object. - * v8::Local object = - * object_template->NewInstance(context).ToLocalChecked(); - * object->SetAlignedPointerInInternalField( - * kV8EmbedderWrapperObjectIndex, - * reinterpret_cast(embedder_object)); - * - * // TODO: Expose {object} where it's necessary. - * } - * \endcode - * - * For instance if {object} is exposed via a global "obj" variable, - * one could write in JS: - * function hot_func() { - * obj.method(42); - * } - * and once {hot_func} gets optimized, CustomEmbedderType::FastMethod - * will be called instead of the slow version, with the following arguments: - * receiver := the {embedder_object} from above - * param := 42 - * - * Currently supported return types: - * - void - * - bool - * - int32_t - * - uint32_t - * - float32_t - * - float64_t - * Currently supported argument types: - * - pointer to an embedder type - * - JavaScript array of primitive types - * - bool - * - int32_t - * - uint32_t - * - int64_t - * - uint64_t - * - float32_t - * - float64_t - * - * The 64-bit integer types currently have the IDL (unsigned) long long - * semantics: https://heycam.github.io/webidl/#abstract-opdef-converttoint - * In the future we'll extend the API to also provide conversions from/to - * BigInt to preserve full precision. - * The floating point types currently have the IDL (unrestricted) semantics, - * which is the only one used by WebGL. We plan to add support also for - * restricted floats/doubles, similarly to the BigInt conversion policies. - * We also differ from the specific NaN bit pattern that WebIDL prescribes - * (https://heycam.github.io/webidl/#es-unrestricted-float) in that Blink - * passes NaN values as-is, i.e. doesn't normalize them. - * - * To be supported types: - * - TypedArrays and ArrayBuffers - * - arrays of embedder types - * - * - * The API offers a limited support for function overloads: - * - * \code - * void FastMethod_2Args(int param, bool another_param); - * void FastMethod_3Args(int param, bool another_param, int third_param); - * - * v8::CFunction fast_method_2args_c_func = - * MakeV8CFunction(FastMethod_2Args); - * v8::CFunction fast_method_3args_c_func = - * MakeV8CFunction(FastMethod_3Args); - * const v8::CFunction fast_method_overloads[] = {fast_method_2args_c_func, - * fast_method_3args_c_func}; - * Local method_template = - * v8::FunctionTemplate::NewWithCFunctionOverloads( - * isolate, SlowCallback, data, signature, length, - * constructor_behavior, side_effect_type, - * {fast_method_overloads, 2}); - * \endcode - * - * In this example a single FunctionTemplate is associated to multiple C++ - * functions. The overload resolution is currently only based on the number of - * arguments passed in a call. For example, if this method_template is - * registered with a wrapper JS object as described above, a call with two - * arguments: - * obj.method(42, true); - * will result in a fast call to FastMethod_2Args, while a call with three or - * more arguments: - * obj.method(42, true, 11); - * will result in a fast call to FastMethod_3Args. Instead a call with less than - * two arguments, like: - * obj.method(42); - * would not result in a fast call but would fall back to executing the - * associated SlowCallback. - */ - -#ifndef INCLUDE_V8_FAST_API_CALLS_H_ -#define INCLUDE_V8_FAST_API_CALLS_H_ - -#include -#include - -#include -#include - -#include "v8-internal.h" // NOLINT(build/include_directory) -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-typed-array.h" // NOLINT(build/include_directory) -#include "v8-value.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Isolate; - -class CTypeInfo { - public: - enum class Type : uint8_t { - kVoid, - kBool, - kUint8, - kInt32, - kUint32, - kInt64, - kUint64, - kFloat32, - kFloat64, - kPointer, - kV8Value, - kSeqOneByteString, - kApiObject, // This will be deprecated once all users have - // migrated from v8::ApiObject to v8::Local. - kAny, // This is added to enable untyped representation of fast - // call arguments for test purposes. It can represent any of - // the other types stored in the same memory as a union (see - // the AnyCType struct declared below). This allows for - // uniform passing of arguments w.r.t. their location - // (in a register or on the stack), independent of their - // actual type. It's currently used by the arm64 simulator - // and can be added to the other simulators as well when fast - // calls having both GP and FP params need to be supported. - }; - - // kCallbackOptionsType is not part of the Type enum - // because it is only used internally. Use value 255 that is larger - // than any valid Type enum. - static constexpr Type kCallbackOptionsType = Type(255); - - enum class SequenceType : uint8_t { - kScalar, - kIsSequence, // sequence - kIsTypedArray, // TypedArray of T or any ArrayBufferView if T - // is void - kIsArrayBuffer // ArrayBuffer - }; - - enum class Flags : uint8_t { - kNone = 0, - kAllowSharedBit = 1 << 0, // Must be an ArrayBuffer or TypedArray - kEnforceRangeBit = 1 << 1, // T must be integral - kClampBit = 1 << 2, // T must be integral - kIsRestrictedBit = 1 << 3, // T must be float or double - }; - - explicit constexpr CTypeInfo( - Type type, SequenceType sequence_type = SequenceType::kScalar, - Flags flags = Flags::kNone) - : type_(type), sequence_type_(sequence_type), flags_(flags) {} - - typedef uint32_t Identifier; - explicit constexpr CTypeInfo(Identifier identifier) - : CTypeInfo(static_cast(identifier >> 16), - static_cast((identifier >> 8) & 255), - static_cast(identifier & 255)) {} - constexpr Identifier GetId() const { - return static_cast(type_) << 16 | - static_cast(sequence_type_) << 8 | - static_cast(flags_); - } - - constexpr Type GetType() const { return type_; } - constexpr SequenceType GetSequenceType() const { return sequence_type_; } - constexpr Flags GetFlags() const { return flags_; } - - static constexpr bool IsIntegralType(Type type) { - return type == Type::kUint8 || type == Type::kInt32 || - type == Type::kUint32 || type == Type::kInt64 || - type == Type::kUint64; - } - - static constexpr bool IsFloatingPointType(Type type) { - return type == Type::kFloat32 || type == Type::kFloat64; - } - - static constexpr bool IsPrimitive(Type type) { - return IsIntegralType(type) || IsFloatingPointType(type) || - type == Type::kBool; - } - - private: - Type type_; - SequenceType sequence_type_; - Flags flags_; -}; - -struct FastApiTypedArrayBase { - public: - // Returns the length in number of elements. - size_t V8_EXPORT length() const { return length_; } - // Checks whether the given index is within the bounds of the collection. - void V8_EXPORT ValidateIndex(size_t index) const; - - protected: - size_t length_ = 0; -}; - -template -struct FastApiTypedArray : public FastApiTypedArrayBase { - public: - V8_INLINE T get(size_t index) const { -#ifdef DEBUG - ValidateIndex(index); -#endif // DEBUG - T tmp; - memcpy(&tmp, reinterpret_cast(data_) + index, sizeof(T)); - return tmp; - } - - bool getStorageIfAligned(T** elements) const { - if (reinterpret_cast(data_) % alignof(T) != 0) { - return false; - } - *elements = reinterpret_cast(data_); - return true; - } - - private: - // This pointer should include the typed array offset applied. - // It's not guaranteed that it's aligned to sizeof(T), it's only - // guaranteed that it's 4-byte aligned, so for 8-byte types we need to - // provide a special implementation for reading from it, which hides - // the possibly unaligned read in the `get` method. - void* data_; -}; - -// Any TypedArray. It uses kTypedArrayBit with base type void -// Overloaded args of ArrayBufferView and TypedArray are not supported -// (for now) because the generic “any” ArrayBufferView doesn’t have its -// own instance type. It could be supported if we specify that -// TypedArray always has precedence over the generic ArrayBufferView, -// but this complicates overload resolution. -struct FastApiArrayBufferView { - void* data; - size_t byte_length; -}; - -struct FastApiArrayBuffer { - void* data; - size_t byte_length; -}; - -struct FastOneByteString { - const char* data; - uint32_t length; -}; - -class V8_EXPORT CFunctionInfo { - public: - enum class Int64Representation : uint8_t { - kNumber = 0, // Use numbers to represent 64 bit integers. - kBigInt = 1, // Use BigInts to represent 64 bit integers. - }; - - // Construct a struct to hold a CFunction's type information. - // |return_info| describes the function's return type. - // |arg_info| is an array of |arg_count| CTypeInfos describing the - // arguments. Only the last argument may be of the special type - // CTypeInfo::kCallbackOptionsType. - CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count, - const CTypeInfo* arg_info, - Int64Representation repr = Int64Representation::kNumber); - - const CTypeInfo& ReturnInfo() const { return return_info_; } - - // The argument count, not including the v8::FastApiCallbackOptions - // if present. - unsigned int ArgumentCount() const { - return HasOptions() ? arg_count_ - 1 : arg_count_; - } - - Int64Representation GetInt64Representation() const { return repr_; } - - // |index| must be less than ArgumentCount(). - // Note: if the last argument passed on construction of CFunctionInfo - // has type CTypeInfo::kCallbackOptionsType, it is not included in - // ArgumentCount(). - const CTypeInfo& ArgumentInfo(unsigned int index) const; - - bool HasOptions() const { - // The options arg is always the last one. - return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() == - CTypeInfo::kCallbackOptionsType; - } - - private: - const CTypeInfo return_info_; - const Int64Representation repr_; - const unsigned int arg_count_; - const CTypeInfo* arg_info_; -}; - -struct FastApiCallbackOptions; - -// Provided for testing. -struct AnyCType { - AnyCType() : int64_value(0) {} - - union { - bool bool_value; - int32_t int32_value; - uint32_t uint32_value; - int64_t int64_value; - uint64_t uint64_value; - float float_value; - double double_value; - void* pointer_value; - Local object_value; - Local sequence_value; - const FastApiTypedArray* uint8_ta_value; - const FastApiTypedArray* int32_ta_value; - const FastApiTypedArray* uint32_ta_value; - const FastApiTypedArray* int64_ta_value; - const FastApiTypedArray* uint64_ta_value; - const FastApiTypedArray* float_ta_value; - const FastApiTypedArray* double_ta_value; - const FastOneByteString* string_value; - FastApiCallbackOptions* options_value; - }; -}; - -static_assert( - sizeof(AnyCType) == 8, - "The AnyCType struct should have size == 64 bits, as this is assumed " - "by EffectControlLinearizer."); - -class V8_EXPORT CFunction { - public: - constexpr CFunction() : address_(nullptr), type_info_(nullptr) {} - - const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); } - - const CTypeInfo& ArgumentInfo(unsigned int index) const { - return type_info_->ArgumentInfo(index); - } - - unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); } - - const void* GetAddress() const { return address_; } - CFunctionInfo::Int64Representation GetInt64Representation() const { - return type_info_->GetInt64Representation(); - } - const CFunctionInfo* GetTypeInfo() const { return type_info_; } - - enum class OverloadResolution { kImpossible, kAtRuntime, kAtCompileTime }; - - // Returns whether an overload between this and the given CFunction can - // be resolved at runtime by the RTTI available for the arguments or at - // compile time for functions with different number of arguments. - OverloadResolution GetOverloadResolution(const CFunction* other) { - // Runtime overload resolution can only deal with functions with the - // same number of arguments. Functions with different arity are handled - // by compile time overload resolution though. - if (ArgumentCount() != other->ArgumentCount()) { - return OverloadResolution::kAtCompileTime; - } - - // The functions can only differ by a single argument position. - int diff_index = -1; - for (unsigned int i = 0; i < ArgumentCount(); ++i) { - if (ArgumentInfo(i).GetSequenceType() != - other->ArgumentInfo(i).GetSequenceType()) { - if (diff_index >= 0) { - return OverloadResolution::kImpossible; - } - diff_index = i; - - // We only support overload resolution between sequence types. - if (ArgumentInfo(i).GetSequenceType() == - CTypeInfo::SequenceType::kScalar || - other->ArgumentInfo(i).GetSequenceType() == - CTypeInfo::SequenceType::kScalar) { - return OverloadResolution::kImpossible; - } - } - } - - return OverloadResolution::kAtRuntime; - } - - template - static CFunction Make(F* func) { - return ArgUnwrap::Make(func); - } - - // Provided for testing purposes. - template - static CFunction Make(R (*func)(Args...), - R_Patch (*patching_func)(Args_Patch...)) { - CFunction c_func = ArgUnwrap::Make(func); - static_assert( - sizeof...(Args_Patch) == sizeof...(Args), - "The patching function must have the same number of arguments."); - c_func.address_ = reinterpret_cast(patching_func); - return c_func; - } - - CFunction(const void* address, const CFunctionInfo* type_info); - - private: - const void* address_; - const CFunctionInfo* type_info_; - - template - class ArgUnwrap { - static_assert(sizeof(F) != sizeof(F), - "CFunction must be created from a function pointer."); - }; - - template - class ArgUnwrap { - public: - static CFunction Make(R (*func)(Args...)); - }; -}; - -/** - * A struct which may be passed to a fast call callback, like so: - * \code - * void FastMethodWithOptions(int param, FastApiCallbackOptions& options); - * \endcode - */ -struct FastApiCallbackOptions { - /** - * Creates a new instance of FastApiCallbackOptions for testing purpose. The - * returned instance may be filled with mock data. - */ - static FastApiCallbackOptions CreateForTesting(Isolate* isolate) { - return {false, {0}, nullptr}; - } - - /** - * If the callback wants to signal an error condition or to perform an - * allocation, it must set options.fallback to true and do an early return - * from the fast method. Then V8 checks the value of options.fallback and if - * it's true, falls back to executing the SlowCallback, which is capable of - * reporting the error (either by throwing a JS exception or logging to the - * console) or doing the allocation. It's the embedder's responsibility to - * ensure that the fast callback is idempotent up to the point where error and - * fallback conditions are checked, because otherwise executing the slow - * callback might produce visible side-effects twice. - */ - bool fallback; - - /** - * The `data` passed to the FunctionTemplate constructor, or `undefined`. - * `data_ptr` allows for default constructing FastApiCallbackOptions. - */ - union { - uintptr_t data_ptr; - v8::Local data; - }; - - /** - * When called from WebAssembly, a view of the calling module's memory. - */ - FastApiTypedArray* const wasm_memory; -}; - -namespace internal { - -// Helper to count the number of occurances of `T` in `List` -template -struct count : std::integral_constant {}; -template -struct count - : std::integral_constant::value> {}; -template -struct count : count {}; - -template -class CFunctionInfoImpl : public CFunctionInfo { - static constexpr int kOptionsArgCount = - count(); - static constexpr int kReceiverCount = 1; - - static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1, - "Only one options parameter is supported."); - - static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount, - "The receiver or the options argument is missing."); - - public: - constexpr CFunctionInfoImpl() - : CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders), - arg_info_storage_, Representation), - arg_info_storage_{ArgBuilders::Build()...} { - constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType(); - static_assert(kReturnType == CTypeInfo::Type::kVoid || - kReturnType == CTypeInfo::Type::kBool || - kReturnType == CTypeInfo::Type::kInt32 || - kReturnType == CTypeInfo::Type::kUint32 || - kReturnType == CTypeInfo::Type::kInt64 || - kReturnType == CTypeInfo::Type::kUint64 || - kReturnType == CTypeInfo::Type::kFloat32 || - kReturnType == CTypeInfo::Type::kFloat64 || - kReturnType == CTypeInfo::Type::kPointer || - kReturnType == CTypeInfo::Type::kAny, - "String and api object values are not currently " - "supported return types."); - } - - private: - const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)]; -}; - -template -struct TypeInfoHelper { - static_assert(sizeof(T) != sizeof(T), "This type is not supported"); -}; - -#define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \ - template <> \ - struct TypeInfoHelper { \ - static constexpr CTypeInfo::Flags Flags() { \ - return CTypeInfo::Flags::kNone; \ - } \ - \ - static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \ - static constexpr CTypeInfo::SequenceType SequenceType() { \ - return CTypeInfo::SequenceType::kScalar; \ - } \ - }; - -template -struct CTypeInfoTraits {}; - -#define DEFINE_TYPE_INFO_TRAITS(CType, Enum) \ - template <> \ - struct CTypeInfoTraits { \ - using ctype = CType; \ - }; - -#define PRIMITIVE_C_TYPES(V) \ - V(bool, kBool) \ - V(uint8_t, kUint8) \ - V(int32_t, kInt32) \ - V(uint32_t, kUint32) \ - V(int64_t, kInt64) \ - V(uint64_t, kUint64) \ - V(float, kFloat32) \ - V(double, kFloat64) \ - V(void*, kPointer) - -// Same as above, but includes deprecated types for compatibility. -#define ALL_C_TYPES(V) \ - PRIMITIVE_C_TYPES(V) \ - V(void, kVoid) \ - V(v8::Local, kV8Value) \ - V(v8::Local, kV8Value) \ - V(AnyCType, kAny) - -// ApiObject was a temporary solution to wrap the pointer to the v8::Value. -// Please use v8::Local in new code for the arguments and -// v8::Local for the receiver, as ApiObject will be deprecated. - -ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR) -PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS) - -#undef PRIMITIVE_C_TYPES -#undef ALL_C_TYPES - -#define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA(T, Enum) \ - template <> \ - struct TypeInfoHelper&> { \ - static constexpr CTypeInfo::Flags Flags() { \ - return CTypeInfo::Flags::kNone; \ - } \ - \ - static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \ - static constexpr CTypeInfo::SequenceType SequenceType() { \ - return CTypeInfo::SequenceType::kIsTypedArray; \ - } \ - }; - -#define TYPED_ARRAY_C_TYPES(V) \ - V(uint8_t, kUint8) \ - V(int32_t, kInt32) \ - V(uint32_t, kUint32) \ - V(int64_t, kInt64) \ - V(uint64_t, kUint64) \ - V(float, kFloat32) \ - V(double, kFloat64) - -TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA) - -#undef TYPED_ARRAY_C_TYPES - -template <> -struct TypeInfoHelper> { - static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } - - static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kVoid; } - static constexpr CTypeInfo::SequenceType SequenceType() { - return CTypeInfo::SequenceType::kIsSequence; - } -}; - -template <> -struct TypeInfoHelper> { - static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } - - static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; } - static constexpr CTypeInfo::SequenceType SequenceType() { - return CTypeInfo::SequenceType::kIsTypedArray; - } -}; - -template <> -struct TypeInfoHelper { - static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } - - static constexpr CTypeInfo::Type Type() { - return CTypeInfo::kCallbackOptionsType; - } - static constexpr CTypeInfo::SequenceType SequenceType() { - return CTypeInfo::SequenceType::kScalar; - } -}; - -template <> -struct TypeInfoHelper { - static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } - - static constexpr CTypeInfo::Type Type() { - return CTypeInfo::Type::kSeqOneByteString; - } - static constexpr CTypeInfo::SequenceType SequenceType() { - return CTypeInfo::SequenceType::kScalar; - } -}; - -#define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \ - static_assert(((COND) == 0) || (ASSERTION), MSG) - -} // namespace internal - -template -class V8_EXPORT CTypeInfoBuilder { - public: - using BaseType = T; - - static constexpr CTypeInfo Build() { - constexpr CTypeInfo::Flags kFlags = - MergeFlags(internal::TypeInfoHelper::Flags(), Flags...); - constexpr CTypeInfo::Type kType = internal::TypeInfoHelper::Type(); - constexpr CTypeInfo::SequenceType kSequenceType = - internal::TypeInfoHelper::SequenceType(); - - STATIC_ASSERT_IMPLIES( - uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kAllowSharedBit), - (kSequenceType == CTypeInfo::SequenceType::kIsTypedArray || - kSequenceType == CTypeInfo::SequenceType::kIsArrayBuffer), - "kAllowSharedBit is only allowed for TypedArrays and ArrayBuffers."); - STATIC_ASSERT_IMPLIES( - uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kEnforceRangeBit), - CTypeInfo::IsIntegralType(kType), - "kEnforceRangeBit is only allowed for integral types."); - STATIC_ASSERT_IMPLIES( - uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kClampBit), - CTypeInfo::IsIntegralType(kType), - "kClampBit is only allowed for integral types."); - STATIC_ASSERT_IMPLIES( - uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kIsRestrictedBit), - CTypeInfo::IsFloatingPointType(kType), - "kIsRestrictedBit is only allowed for floating point types."); - STATIC_ASSERT_IMPLIES(kSequenceType == CTypeInfo::SequenceType::kIsSequence, - kType == CTypeInfo::Type::kVoid, - "Sequences are only supported from void type."); - STATIC_ASSERT_IMPLIES( - kSequenceType == CTypeInfo::SequenceType::kIsTypedArray, - CTypeInfo::IsPrimitive(kType) || kType == CTypeInfo::Type::kVoid, - "TypedArrays are only supported from primitive types or void."); - - // Return the same type with the merged flags. - return CTypeInfo(internal::TypeInfoHelper::Type(), - internal::TypeInfoHelper::SequenceType(), kFlags); - } - - private: - template - static constexpr CTypeInfo::Flags MergeFlags(CTypeInfo::Flags flags, - Rest... rest) { - return CTypeInfo::Flags(uint8_t(flags) | uint8_t(MergeFlags(rest...))); - } - static constexpr CTypeInfo::Flags MergeFlags() { return CTypeInfo::Flags(0); } -}; - -namespace internal { -template -class CFunctionBuilderWithFunction { - public: - explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {} - - template - constexpr auto Ret() { - return CFunctionBuilderWithFunction< - CTypeInfoBuilder, - ArgBuilders...>(fn_); - } - - template - constexpr auto Arg() { - // Return a copy of the builder with the Nth arg builder merged with - // template parameter pack Flags. - return ArgImpl( - std::make_index_sequence()); - } - - // Provided for testing purposes. - template - auto Patch(Ret (*patching_func)(Args...)) { - static_assert( - sizeof...(Args) == sizeof...(ArgBuilders), - "The patching function must have the same number of arguments."); - fn_ = reinterpret_cast(patching_func); - return *this; - } - - template - auto Build() { - static CFunctionInfoImpl - instance; - return CFunction(fn_, &instance); - } - - private: - template - struct GetArgBuilder; - - // Returns the same ArgBuilder as the one at index N, including its flags. - // Flags in the template parameter pack are ignored. - template - struct GetArgBuilder { - using type = - typename std::tuple_element>::type; - }; - - // Returns an ArgBuilder with the same base type as the one at index N, - // but merges the flags with the flags in the template parameter pack. - template - struct GetArgBuilder { - using type = CTypeInfoBuilder< - typename std::tuple_element>::type::BaseType, - std::tuple_element>::type::Build() - .GetFlags(), - Flags...>; - }; - - // Return a copy of the CFunctionBuilder, but merges the Flags on - // ArgBuilder index N with the new Flags passed in the template parameter - // pack. - template - constexpr auto ArgImpl(std::index_sequence) { - return CFunctionBuilderWithFunction< - RetBuilder, typename GetArgBuilder::type...>(fn_); - } - - const void* fn_; -}; - -class CFunctionBuilder { - public: - constexpr CFunctionBuilder() {} - - template - constexpr auto Fn(R (*fn)(Args...)) { - return CFunctionBuilderWithFunction, - CTypeInfoBuilder...>( - reinterpret_cast(fn)); - } -}; - -} // namespace internal - -// static -template -CFunction CFunction::ArgUnwrap::Make(R (*func)(Args...)) { - return internal::CFunctionBuilder().Fn(func).Build(); -} - -using CFunctionBuilder = internal::CFunctionBuilder; - -static constexpr CTypeInfo kTypeInfoInt32 = CTypeInfo(CTypeInfo::Type::kInt32); -static constexpr CTypeInfo kTypeInfoFloat64 = - CTypeInfo(CTypeInfo::Type::kFloat64); - -/** - * Copies the contents of this JavaScript array to a C++ buffer with - * a given max_length. A CTypeInfo is passed as an argument, - * instructing different rules for conversion (e.g. restricted float/double). - * The element type T of the destination array must match the C type - * corresponding to the CTypeInfo (specified by CTypeInfoTraits). - * If the array length is larger than max_length or the array is of - * unsupported type, the operation will fail, returning false. Generally, an - * array which contains objects, undefined, null or anything not convertible - * to the requested destination type, is considered unsupported. The operation - * returns true on success. `type_info` will be used for conversions. - */ -template -bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer( - Local src, T* dst, uint32_t max_length); - -template <> -bool V8_EXPORT V8_WARN_UNUSED_RESULT -TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), - int32_t>(Local src, int32_t* dst, - uint32_t max_length); - -template <> -bool V8_EXPORT V8_WARN_UNUSED_RESULT -TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), - uint32_t>(Local src, uint32_t* dst, - uint32_t max_length); - -template <> -bool V8_EXPORT V8_WARN_UNUSED_RESULT -TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), - float>(Local src, float* dst, - uint32_t max_length); - -template <> -bool V8_EXPORT V8_WARN_UNUSED_RESULT -TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), - double>(Local src, double* dst, - uint32_t max_length); - -} // namespace v8 - -#endif // INCLUDE_V8_FAST_API_CALLS_H_ diff --git a/NativeScript/napi/v8/include/v8-forward.h b/NativeScript/napi/v8/include/v8-forward.h deleted file mode 100644 index db3a2017..00000000 --- a/NativeScript/napi/v8/include/v8-forward.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_FORWARD_H_ -#define INCLUDE_V8_FORWARD_H_ - -// This header is intended to be used by headers that pass around V8 types, -// either by pointer or using Local. The full definitions can be included -// either via v8.h or the more fine-grained headers. - -#include "v8-local-handle.h" // NOLINT(build/include_directory) - -namespace v8 { - -class AccessorSignature; -class Array; -class ArrayBuffer; -class ArrayBufferView; -class BigInt; -class BigInt64Array; -class BigIntObject; -class BigUint64Array; -class Boolean; -class BooleanObject; -class Context; -class DataView; -class Data; -class Date; -class Extension; -class External; -class FixedArray; -class Float32Array; -class Float64Array; -class Function; -template -class FunctionCallbackInfo; -class FunctionTemplate; -class Int16Array; -class Int32; -class Int32Array; -class Int8Array; -class Integer; -class Isolate; -class Map; -class Module; -class Name; -class Number; -class NumberObject; -class Object; -class ObjectTemplate; -class Platform; -class Primitive; -class Private; -class Promise; -class Proxy; -class RegExp; -class Script; -class Set; -class SharedArrayBuffer; -class Signature; -class String; -class StringObject; -class Symbol; -class SymbolObject; -class Template; -class TryCatch; -class TypedArray; -class Uint16Array; -class Uint32; -class Uint32Array; -class Uint8Array; -class Uint8ClampedArray; -class UnboundModuleScript; -class Value; -class WasmMemoryObject; -class WasmModuleObject; - -} // namespace v8 - -#endif // INCLUDE_V8_FORWARD_H_ diff --git a/NativeScript/napi/v8/include/v8-function-callback.h b/NativeScript/napi/v8/include/v8-function-callback.h deleted file mode 100644 index 17b37cdd..00000000 --- a/NativeScript/napi/v8/include/v8-function-callback.h +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_FUNCTION_CALLBACK_H_ -#define INCLUDE_V8_FUNCTION_CALLBACK_H_ - -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-primitive.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -template -class BasicTracedReference; -template -class Global; -class Object; -class Value; - -namespace internal { -class FunctionCallbackArguments; -class PropertyCallbackArguments; -class Builtins; -} // namespace internal - -namespace debug { -class ConsoleCallArguments; -} // namespace debug - -template -class ReturnValue { - public: - template - V8_INLINE ReturnValue(const ReturnValue& that) : value_(that.value_) { - static_assert(std::is_base_of::value, "type check"); - } - // Local setters - template - V8_INLINE void Set(const Global& handle); - template - V8_INLINE void Set(const BasicTracedReference& handle); - template - V8_INLINE void Set(const Local handle); - // Fast primitive setters - V8_INLINE void Set(bool value); - V8_INLINE void Set(double i); - V8_INLINE void Set(int32_t i); - V8_INLINE void Set(uint32_t i); - // Fast JS primitive setters - V8_INLINE void SetNull(); - V8_INLINE void SetUndefined(); - V8_INLINE void SetEmptyString(); - // Convenience getter for Isolate - V8_INLINE Isolate* GetIsolate() const; - - // Pointer setter: Uncompilable to prevent inadvertent misuse. - template - V8_INLINE void Set(S* whatever); - - // Getter. Creates a new Local<> so it comes with a certain performance - // hit. If the ReturnValue was not yet set, this will return the undefined - // value. - V8_INLINE Local Get() const; - - private: - template - friend class ReturnValue; - template - friend class FunctionCallbackInfo; - template - friend class PropertyCallbackInfo; - template - friend class PersistentValueMapBase; - V8_INLINE void SetInternal(internal::Address value) { *value_ = value; } - V8_INLINE internal::Address GetDefaultValue(); - V8_INLINE explicit ReturnValue(internal::Address* slot); - - // See FunctionCallbackInfo. - static constexpr int kIsolateValueIndex = -2; - - internal::Address* value_; -}; - -/** - * The argument information given to function call callbacks. This - * class provides access to information about the context of the call, - * including the receiver, the number and values of arguments, and - * the holder of the function. - */ -template -class FunctionCallbackInfo { - public: - /** The number of available arguments. */ - V8_INLINE int Length() const; - /** - * Accessor for the available arguments. Returns `undefined` if the index - * is out of bounds. - */ - V8_INLINE Local operator[](int i) const; - /** Returns the receiver. This corresponds to the "this" value. */ - V8_INLINE Local This() const; - /** - * If the callback was created without a Signature, this is the same - * value as This(). If there is a signature, and the signature didn't match - * This() but one of its hidden prototypes, this will be the respective - * hidden prototype. - * - * Note that this is not the prototype of This() on which the accessor - * referencing this callback was found (which in V8 internally is often - * referred to as holder [sic]). - */ - V8_INLINE Local Holder() const; - /** For construct calls, this returns the "new.target" value. */ - V8_INLINE Local NewTarget() const; - /** Indicates whether this is a regular call or a construct call. */ - V8_INLINE bool IsConstructCall() const; - /** The data argument specified when creating the callback. */ - V8_INLINE Local Data() const; - /** The current Isolate. */ - V8_INLINE Isolate* GetIsolate() const; - /** The ReturnValue for the call. */ - V8_INLINE ReturnValue GetReturnValue() const; - - private: - friend class internal::FunctionCallbackArguments; - friend class internal::CustomArguments; - friend class debug::ConsoleCallArguments; - - static constexpr int kHolderIndex = 0; - static constexpr int kIsolateIndex = 1; - static constexpr int kUnusedIndex = 2; - static constexpr int kReturnValueIndex = 3; - static constexpr int kDataIndex = 4; - static constexpr int kNewTargetIndex = 5; - static constexpr int kArgsLength = 6; - - static constexpr int kArgsLengthWithReceiver = kArgsLength + 1; - - // Codegen constants: - static constexpr int kSize = 3 * internal::kApiSystemPointerSize; - static constexpr int kImplicitArgsOffset = 0; - static constexpr int kValuesOffset = - kImplicitArgsOffset + internal::kApiSystemPointerSize; - static constexpr int kLengthOffset = - kValuesOffset + internal::kApiSystemPointerSize; - - static constexpr int kThisValuesIndex = -1; - static_assert(ReturnValue::kIsolateValueIndex == - kIsolateIndex - kReturnValueIndex); - - V8_INLINE FunctionCallbackInfo(internal::Address* implicit_args, - internal::Address* values, int length); - internal::Address* implicit_args_; - internal::Address* values_; - int length_; -}; - -/** - * The information passed to a property callback about the context - * of the property access. - */ -template -class PropertyCallbackInfo { - public: - /** - * \return The isolate of the property access. - */ - V8_INLINE Isolate* GetIsolate() const; - - /** - * \return The data set in the configuration, i.e., in - * `NamedPropertyHandlerConfiguration` or - * `IndexedPropertyHandlerConfiguration.` - */ - V8_INLINE Local Data() const; - - /** - * \return The receiver. In many cases, this is the object on which the - * property access was intercepted. When using - * `Reflect.get`, `Function.prototype.call`, or similar functions, it is the - * object passed in as receiver or thisArg. - * - * \code - * void GetterCallback(Local name, - * const v8::PropertyCallbackInfo& info) { - * auto context = info.GetIsolate()->GetCurrentContext(); - * - * v8::Local a_this = - * info.This() - * ->GetRealNamedProperty(context, v8_str("a")) - * .ToLocalChecked(); - * v8::Local a_holder = - * info.Holder() - * ->GetRealNamedProperty(context, v8_str("a")) - * .ToLocalChecked(); - * - * CHECK(v8_str("r")->Equals(context, a_this).FromJust()); - * CHECK(v8_str("obj")->Equals(context, a_holder).FromJust()); - * - * info.GetReturnValue().Set(name); - * } - * - * v8::Local templ = - * v8::FunctionTemplate::New(isolate); - * templ->InstanceTemplate()->SetHandler( - * v8::NamedPropertyHandlerConfiguration(GetterCallback)); - * LocalContext env; - * env->Global() - * ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) - * .ToLocalChecked() - * ->NewInstance(env.local()) - * .ToLocalChecked()) - * .FromJust(); - * - * CompileRun("obj.a = 'obj'; var r = {a: 'r'}; Reflect.get(obj, 'x', r)"); - * \endcode - */ - V8_INLINE Local This() const; - - /** - * \return The object in the prototype chain of the receiver that has the - * interceptor. Suppose you have `x` and its prototype is `y`, and `y` - * has an interceptor. Then `info.This()` is `x` and `info.Holder()` is `y`. - * The Holder() could be a hidden object (the global object, rather - * than the global proxy). - * - * \note For security reasons, do not pass the object back into the runtime. - */ - V8_INLINE Local Holder() const; - - /** - * \return The return value of the callback. - * Can be changed by calling Set(). - * \code - * info.GetReturnValue().Set(...) - * \endcode - * - */ - V8_INLINE ReturnValue GetReturnValue() const; - - /** - * \return True if the intercepted function should throw if an error occurs. - * Usually, `true` corresponds to `'use strict'`. - * - * \note Always `false` when intercepting `Reflect.set()` - * independent of the language mode. - */ - V8_INLINE bool ShouldThrowOnError() const; - - private: - friend class MacroAssembler; - friend class internal::PropertyCallbackArguments; - friend class internal::CustomArguments; - static constexpr int kShouldThrowOnErrorIndex = 0; - static constexpr int kHolderIndex = 1; - static constexpr int kIsolateIndex = 2; - static constexpr int kUnusedIndex = 3; - static constexpr int kReturnValueIndex = 4; - static constexpr int kDataIndex = 5; - static constexpr int kThisIndex = 6; - static constexpr int kArgsLength = 7; - - static constexpr int kSize = 1 * internal::kApiSystemPointerSize; - - V8_INLINE explicit PropertyCallbackInfo(internal::Address* args) - : args_(args) {} - - internal::Address* args_; -}; - -using FunctionCallback = void (*)(const FunctionCallbackInfo& info); - -// --- Implementation --- - -template -ReturnValue::ReturnValue(internal::Address* slot) : value_(slot) {} - -template -template -void ReturnValue::Set(const Global& handle) { - static_assert(std::is_base_of::value, "type check"); - if (V8_UNLIKELY(handle.IsEmpty())) { - *value_ = GetDefaultValue(); - } else { - *value_ = handle.ptr(); - } -} - -template -template -void ReturnValue::Set(const BasicTracedReference& handle) { - static_assert(std::is_base_of::value, "type check"); - if (V8_UNLIKELY(handle.IsEmpty())) { - *value_ = GetDefaultValue(); - } else { - *value_ = handle.ptr(); - } -} - -template -template -void ReturnValue::Set(const Local handle) { - static_assert(std::is_void::value || std::is_base_of::value, - "type check"); - if (V8_UNLIKELY(handle.IsEmpty())) { - *value_ = GetDefaultValue(); - } else { - *value_ = handle.ptr(); - } -} - -template -void ReturnValue::Set(double i) { - static_assert(std::is_base_of::value, "type check"); - Set(Number::New(GetIsolate(), i)); -} - -template -void ReturnValue::Set(int32_t i) { - static_assert(std::is_base_of::value, "type check"); - using I = internal::Internals; - if (V8_LIKELY(I::IsValidSmi(i))) { - *value_ = I::IntToSmi(i); - return; - } - Set(Integer::New(GetIsolate(), i)); -} - -template -void ReturnValue::Set(uint32_t i) { - static_assert(std::is_base_of::value, "type check"); - // Can't simply use INT32_MAX here for whatever reason. - bool fits_into_int32_t = (i & (1U << 31)) == 0; - if (V8_LIKELY(fits_into_int32_t)) { - Set(static_cast(i)); - return; - } - Set(Integer::NewFromUnsigned(GetIsolate(), i)); -} - -template -void ReturnValue::Set(bool value) { - static_assert(std::is_base_of::value, "type check"); - using I = internal::Internals; - int root_index; - if (value) { - root_index = I::kTrueValueRootIndex; - } else { - root_index = I::kFalseValueRootIndex; - } - *value_ = I::GetRoot(GetIsolate(), root_index); -} - -template -void ReturnValue::SetNull() { - static_assert(std::is_base_of::value, "type check"); - using I = internal::Internals; - *value_ = I::GetRoot(GetIsolate(), I::kNullValueRootIndex); -} - -template -void ReturnValue::SetUndefined() { - static_assert(std::is_base_of::value, "type check"); - using I = internal::Internals; - *value_ = I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex); -} - -template -void ReturnValue::SetEmptyString() { - static_assert(std::is_base_of::value, "type check"); - using I = internal::Internals; - *value_ = I::GetRoot(GetIsolate(), I::kEmptyStringRootIndex); -} - -template -Isolate* ReturnValue::GetIsolate() const { - return *reinterpret_cast(&value_[kIsolateValueIndex]); -} - -template -Local ReturnValue::Get() const { - using I = internal::Internals; -#if V8_STATIC_ROOTS_BOOL - if (I::is_identical(*value_, I::StaticReadOnlyRoot::kTheHoleValue)) { -#else - if (*value_ == I::GetRoot(GetIsolate(), I::kTheHoleValueRootIndex)) { -#endif - return Undefined(GetIsolate()); - } - return Local::New(GetIsolate(), reinterpret_cast(value_)); -} - -template -template -void ReturnValue::Set(S* whatever) { - static_assert(sizeof(S) < 0, "incompilable to prevent inadvertent misuse"); -} - -template -internal::Address ReturnValue::GetDefaultValue() { - using I = internal::Internals; - return I::GetRoot(GetIsolate(), I::kTheHoleValueRootIndex); -} - -template -FunctionCallbackInfo::FunctionCallbackInfo(internal::Address* implicit_args, - internal::Address* values, - int length) - : implicit_args_(implicit_args), values_(values), length_(length) {} - -template -Local FunctionCallbackInfo::operator[](int i) const { - // values_ points to the first argument (not the receiver). - if (i < 0 || length_ <= i) return Undefined(GetIsolate()); - return Local::FromSlot(values_ + i); -} - -template -Local FunctionCallbackInfo::This() const { - // values_ points to the first argument (not the receiver). - return Local::FromSlot(values_ + kThisValuesIndex); -} - -template -Local FunctionCallbackInfo::Holder() const { - return Local::FromSlot(&implicit_args_[kHolderIndex]); -} - -template -Local FunctionCallbackInfo::NewTarget() const { - return Local::FromSlot(&implicit_args_[kNewTargetIndex]); -} - -template -Local FunctionCallbackInfo::Data() const { - return Local::FromSlot(&implicit_args_[kDataIndex]); -} - -template -Isolate* FunctionCallbackInfo::GetIsolate() const { - return *reinterpret_cast(&implicit_args_[kIsolateIndex]); -} - -template -ReturnValue FunctionCallbackInfo::GetReturnValue() const { - return ReturnValue(&implicit_args_[kReturnValueIndex]); -} - -template -bool FunctionCallbackInfo::IsConstructCall() const { - return !NewTarget()->IsUndefined(); -} - -template -int FunctionCallbackInfo::Length() const { - return length_; -} - -template -Isolate* PropertyCallbackInfo::GetIsolate() const { - return *reinterpret_cast(&args_[kIsolateIndex]); -} - -template -Local PropertyCallbackInfo::Data() const { - return Local::FromSlot(&args_[kDataIndex]); -} - -template -Local PropertyCallbackInfo::This() const { - return Local::FromSlot(&args_[kThisIndex]); -} - -template -Local PropertyCallbackInfo::Holder() const { - return Local::FromSlot(&args_[kHolderIndex]); -} - -template -ReturnValue PropertyCallbackInfo::GetReturnValue() const { - return ReturnValue(&args_[kReturnValueIndex]); -} - -template -bool PropertyCallbackInfo::ShouldThrowOnError() const { - using I = internal::Internals; - if (args_[kShouldThrowOnErrorIndex] != - I::IntToSmi(I::kInferShouldThrowMode)) { - return args_[kShouldThrowOnErrorIndex] != I::IntToSmi(I::kDontThrow); - } - return v8::internal::ShouldThrowOnError( - reinterpret_cast(GetIsolate())); -} - -} // namespace v8 - -#endif // INCLUDE_V8_FUNCTION_CALLBACK_H_ diff --git a/NativeScript/napi/v8/include/v8-function.h b/NativeScript/napi/v8/include/v8-function.h deleted file mode 100644 index 30a9fcfe..00000000 --- a/NativeScript/napi/v8/include/v8-function.h +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2021 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_FUNCTION_H_ -#define INCLUDE_V8_FUNCTION_H_ - -#include -#include - -#include "v8-function-callback.h" // NOLINT(build/include_directory) -#include "v8-local-handle.h" // NOLINT(build/include_directory) -#include "v8-message.h" // NOLINT(build/include_directory) -#include "v8-object.h" // NOLINT(build/include_directory) -#include "v8-template.h" // NOLINT(build/include_directory) -#include "v8config.h" // NOLINT(build/include_directory) - -namespace v8 { - -class Context; -class UnboundScript; - -/** - * A JavaScript function object (ECMA-262, 15.3). - */ -class V8_EXPORT Function : public Object { - public: - /** - * Create a function in the current execution context - * for a given FunctionCallback. - */ - static MaybeLocal New( - Local context, FunctionCallback callback, - Local data = Local(), int length = 0, - ConstructorBehavior behavior = ConstructorBehavior::kAllow, - SideEffectType side_effect_type = SideEffectType::kHasSideEffect); - - V8_WARN_UNUSED_RESULT MaybeLocal NewInstance( - Local context, int argc, Local argv[]) const; - - V8_WARN_UNUSED_RESULT MaybeLocal NewInstance( - Local context) const { - return NewInstance(context, 0, nullptr); - } - - /** - * When side effect checks are enabled, passing kHasNoSideEffect allows the - * constructor to be invoked without throwing. Calls made within the - * constructor are still checked. - */ - V8_WARN_UNUSED_RESULT MaybeLocal NewInstanceWithSideEffectType( - Local context, int argc, Local argv[], - SideEffectType side_effect_type = SideEffectType::kHasSideEffect) const; - - V8_WARN_UNUSED_RESULT MaybeLocal Call(Local context, - Local recv, int argc, - Local argv[]); - - void SetName(Local name); - Local GetName() const; - - V8_DEPRECATED("No direct replacement") - MaybeLocal GetUnboundScript() const; - - /** - * Name inferred from variable or property assignment of this function. - * Used to facilitate debugging and profiling of JavaScript code written - * in an OO style, where many functions are anonymous but are assigned - * to object properties. - */ - Local GetInferredName() const; - - /** - * displayName if it is set, otherwise name if it is configured, otherwise - * function name, otherwise inferred name. - */ - Local GetDebugName() const; - - /** - * Returns zero based line number of function body and - * kLineOffsetNotFound if no information available. - */ - int GetScriptLineNumber() const; - /** - * Returns zero based column number of function body and - * kLineOffsetNotFound if no information available. - */ - int GetScriptColumnNumber() const; - - /** - * Returns zero based start position (character offset) of function body and - * kLineOffsetNotFound if no information available. - */ - int GetScriptStartPosition() const; - - /** - * Returns scriptId. - */ - int ScriptId() const; - - /** - * Returns the original function if this function is bound, else returns - * v8::Undefined. - */ - Local GetBoundFunction() const; - - /** - * Calls builtin Function.prototype.toString on this function. - * This is different from Value::ToString() that may call a user-defined - * toString() function, and different than Object::ObjectProtoToString() which - * always serializes "[object Function]". - */ - V8_WARN_UNUSED_RESULT MaybeLocal FunctionProtoToString( - Local context); - - /** - * Returns true if the function does nothing. - * The function returns false on error. - * Note that this function is experimental. Embedders should not rely on - * this existing. We may remove this function in the future. - */ - V8_WARN_UNUSED_RESULT bool Experimental_IsNopFunction() const; - - ScriptOrigin GetScriptOrigin() const; - V8_INLINE static Function* Cast(Value* value) { -#ifdef V8_ENABLE_CHECKS - CheckCast(value); -#endif - return static_cast(value); - } - - static const int kLineOffsetNotFound; - - private: - Function(); - static void CheckCast(Value* obj); -}; -} // namespace v8 - -#endif // INCLUDE_V8_FUNCTION_H_ diff --git a/NativeScript/napi/v8/include/v8-handle-base.h b/NativeScript/napi/v8/include/v8-handle-base.h deleted file mode 100644 index d346a80d..00000000 --- a/NativeScript/napi/v8/include/v8-handle-base.h +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2023 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef INCLUDE_V8_HANDLE_BASE_H_ -#define INCLUDE_V8_HANDLE_BASE_H_ - -#include "v8-internal.h" // NOLINT(build/include_directory) - -namespace v8 { - -namespace internal { - -// Helper functions about values contained in handles. -// A value is either an indirect pointer or a direct pointer, depending on -// whether direct local support is enabled. -class ValueHelper final { - public: -#ifdef V8_ENABLE_DIRECT_LOCAL - static constexpr Address kTaggedNullAddress = 1; - static constexpr Address kEmpty = kTaggedNullAddress; -#else - static constexpr Address kEmpty = kNullAddress; -#endif // V8_ENABLE_DIRECT_LOCAL - - template - V8_INLINE static bool IsEmpty(T* value) { - return reinterpret_cast
(value) == kEmpty; - } - - // Returns a handle's "value" for all kinds of abstract handles. For Local, - // it is equivalent to `*handle`. The variadic parameters support handle - // types with extra type parameters, like `Persistent`. - template